⚡ 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 (v18 or higher)
- Docker and Docker Compose (for MongoDB)
- npm or yarn (package manager)
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.21.2 @nodeblocks/backend-sdk@^0.14.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: These docs were verified against
@nodeblocks/backend-sdk0.14.0 and Express 4.21.x. The SDK requires TypeScript 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.14.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.
🖼️ 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