⚡ Quickstart
This hands-on tutorial gets you from zero to running API in less than 5 minutes.
We will create a minimal Express server exposing the ready-made Authentication Service and Profile Service backed by MongoDB.
📋 Prerequisites
Before we start, make sure you have the following installed on your system:
- Node.js 22.x or 24.x
- Docker and Docker Compose (for MongoDB)
- npm or yarn (package manager)
The SDK uses Pino 10, which dropped Node.js 18 support. Use one of the Node.js versions listed above for this tutorial.
To install and learn about Docker Compose, go to https://docs.docker.com/compose/.
1️⃣ Create Project Directory
First, create a new directory for your quickstart project and navigate into it:
mkdir quickstart
cd quickstart
2️⃣ Install Dependencies
npm install express@^4.22.3 @nodeblocks/backend-sdk@^0.15.0
npm install -D typescript @types/node @types/express@^4.17.0
📝 Important: Don't forget to add your
.npmrcfile with the authentication token to access the private @nodeblocks/backend-sdk package.
⚠️ Compatibility: This tutorial targets
@nodeblocks/backend-sdk0.15.0 and Express 4.22.x, matching the SDK's declared Express dependency. TypeScript is required for this tutorial.
Cookie Authentication Dependencies
The quickstart uses bearer authentication by default. If you set authMode: 'cookie', also install cookie-parser and its TypeScript declarations:
npm install cookie-parser
npm install -D @types/cookie-parser
Using Local Development Version
If you already have the nodeblocks-backend-sdk repository checked out next to your project (for example in a monorepo), build and link it locally:
cd /path/to/nodeblocks-backend-sdk
npm install
npm run build
npm link
cd /path/to/quickstart
npm link @nodeblocks/backend-sdk
To switch back to the published package:
npm unlink @nodeblocks/backend-sdk
npm install @nodeblocks/backend-sdk@^0.15.0
3️⃣ Initialize TypeScript
Create a TypeScript configuration file to enable proper compilation and type checking:
npx tsc --init
Update the generated tsconfig.json so the default Express import in this tutorial compiles:
{
"compilerOptions": {
"target": "ES2020",
"module": "CommonJS",
"moduleResolution": "Node",
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true
}
}
4️⃣ Setup MongoDB with Docker Compose
Create docker-compose.yml in your project root directory with following content:
services:
mongodb:
image: mongo:7
container_name: mongodb
ports:
- '27017:27017'
restart: always
environment:
MONGO_INITDB_DATABASE: dev
MONGO_INITDB_ROOT_USERNAME: user
MONGO_INITDB_ROOT_PASSWORD: password
💡 Note:
MONGO_INITDB_ROOT_*creates a root user in theadmindatabase. ThewithMongoconnection URL below includesauthSource=adminso authentication succeeds when connecting to thedevdatabase. Username and password must match the Docker Compose values.
Start MongoDB in detached mode:
docker compose up -d
5️⃣ Bootstrap the Server
Create /index.ts with the minimal setup to run both authentication and profile services:
import express from 'express';
import {middlewares, services, drivers} from '@nodeblocks/backend-sdk';
const {nodeBlocksErrorMiddleware} = middlewares;
const {authService, profileService} = services;
const {withMongo} = drivers;
const connectToDatabase = withMongo('mongodb://localhost:27017/?authSource=admin', 'dev', 'user', 'password');
async function main() {
// Authentication Service - handles registration, login, and token management
express()
.use(
authService({
...(await connectToDatabase('identities')),
...(await connectToDatabase('refreshtokens')),
}, {
authSecrets: {
authEncSecret: 'your-encryption-secret',
authSignSecret: 'your-signing-secret',
},
identity: {
typeIds: {
admin: '100',
guest: '000',
regular: '010',
},
},
}),
profileService(
{
...(await connectToDatabase('profiles')),
...(await connectToDatabase('identities')),
...(await connectToDatabase('organizations')),
...(await connectToDatabase('products')),
},
{
authSecrets: {
authEncSecret: 'your-encryption-secret',
authSignSecret: 'your-signing-secret',
},
identity: {
typeIds: {
admin: '100',
guest: '000',
regular: '010',
},
},
},
),
)
.use(nodeBlocksErrorMiddleware())
.listen(8089, () => console.log('Server running on http://localhost:8089'));
}
main().catch(error => {
console.error(error);
process.exit(1);
});
⚠️ Critical: The
nodeBlocksErrorMiddleware()is essential! Without it, your API will return HTML error pages instead of JSON responses when errors occur. Always include it after your services.
🔑 Authentication Setup: This quickstart configures the
identitiesandrefreshtokensdatastores, needed for registration, login, refresh-token, and logout flows.authServicealso mounts invitation, one-time-token, email, and OAuth routes; enable those only after providing their required datastores and drivers (for example,invitations,onetimetokens, mail, and OAuth drivers). The profile service requires valid authentication tokens to access its endpoints.
To use OAuth initiation and callback routes, also set a nonempty oauth.allowedRedirectOrigins allowlist in the second authService argument. Use frontend destination origins, including the port for local development; see OAuth redirect origins. Credential authentication in this quickstart does not require this setting.
🖼️ Profile scope: This example intentionally supports profiles without avatars.
profileServicealso mounts avatar upload and avatar-normalization routes, which require afileStorageDriver; do not send anavatarfield or use avatar endpoints until you configure that driver. Follow/like routes are also outside this quickstart.
Using Cookie Authentication
For cookie authentication, add import cookieParser from 'cookie-parser';, call .use(cookieParser()) before the two service calls, and set authMode: 'cookie' in both service configuration objects from the main example.
The SDK reads authentication tokens from req.cookies; it does not register cookie-parser for the host application.
6️⃣ Build and Run the Server
Compile the TypeScript code and start the server:
npx tsc
node index.js
You should see the following output:
Server running on http://localhost:8089
Your API server is now running with both authentication and profile services!
7️⃣ Test the Authentication Flow
Step 1: Register
First, create an account through the authentication service:
curl -X POST http://localhost:8089/auth/register \
-H 'Content-Type: application/json' \
-d '{
"email": "user@example.com",
"password": "securepassword123"
}'
You should receive a 201 Created response with an empty body.
Note: Registration does not return the identity id in the response body. Use the
idfield from the login response (Step 2) asidentityIdwhen creating a profile.
Step 2: Login to Get Access Token
Login with the registered identity to get an access token and identity id:
curl -X POST http://localhost:8089/auth/login \
-H 'Content-Type: application/json' \
-d '{
"email": "user@example.com",
"password": "securepassword123"
}'
You should receive a response with an access token, refresh token, and identity id:
{
"accessToken": "77c268e06d87594067d91c5b2f08e532...",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"id": "f792cde5-958b-49e9-bf83-13d59c1e35c0"
}
Step 3: Use the Access Token
Now you can use the access token and the login response id to access the profile service endpoints:
# Create profile (identityId = id from login response)
curl -X POST http://localhost:8089/profiles \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-d '{"name":"John","identityId":"f792cde5-958b-49e9-bf83-13d59c1e35c0"}'
You should receive a JSON response similar to the following:
{
"id": "878fc020-b313-42a9-bdc6-4946b9be598e",
"identityId": "f792cde5-958b-49e9-bf83-13d59c1e35c0",
"name": "John",
"organizationFollows": [],
"productLikes": [],
"profileFollows": [],
"createdAt": "2025-06-18T06:19:55.974Z",
"updatedAt": "2025-06-18T06:19:55.974Z"
}
🚨 Understanding Error Handling
Try making an invalid request to see the error middleware in action:
# Missing required fields - triggers validation error
curl -X POST http://localhost:8089/profiles \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <bearer-token-from-auth-service>' \
-d '{"identityId":"f792cde5-958b-49e9-bf83-13d59c1e35c0"}'
With nodeBlocksErrorMiddleware() (✅ Correct):
{
"error": {
"message": "Validation Error",
"data": ["request body must have required property 'name'"]
}
}
Without nodeBlocksErrorMiddleware() (❌ Wrong):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Error</title>
</head>
<body>
<pre>NodeblocksError: Validation Error<br> --error stack--</pre>
</body>
</html>
💡 Key Point: The error middleware transforms all errors into consistent JSON responses, making your API client-friendly.
🎉 What You've Built
Congratulations! ✨ You now have a working REST API with (subset of the mounted services):
- Authentication system with registration and login
- JWT token management with access and refresh tokens
- CRUD operations for profiles (Create, Read, Update, Delete)
- Automatic validation using JSON Schema
- MongoDB integration
- JSON error responses with proper HTTP status codes
- TypeScript support for type safety
Production note:
withMongocreates a new MongoDB client for each collection request. This is convenient for the quickstart, but production applications should create oneMongoClient, reuse its collections across services, and close it during graceful shutdown.
🔗 Available Endpoints
Authentication Service (/auth)
POST /auth/register- Register a new identityPOST /auth/login- Login and get access tokenPOST /auth/logout- Logout and invalidate tokens (requires auth; bearer mode needsrefreshTokenin the body)
Profile Service (/profiles) - Requires Authentication
POST /profiles- Create a new profile (admin or self)GET /profiles- List all profiles (admin only)GET /profiles/:profileId- Get profile by ID (admin or profile owner)PATCH /profiles/:profileId- Update profile (admin or profile owner)DELETE /profiles/:profileId- Delete profile (admin or profile owner)
🔐 Authentication Flow
- Register via
/auth/register - Login to get access and refresh tokens via
/auth/login - Use access token in
Authorization: Bearer <token>header for protected endpoints
➡️ Next Steps
- Create a Custom Service - Learn to build your own services
- Schema Concepts - Understand data validation
- Handler Patterns - Master business logic organization