Skip to main content
Version: 🚧 Canary

⚡ 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 .npmrc file with the authentication token to access the private @nodeblocks/backend-sdk package.

⚠️ Compatibility: These docs were verified against @nodeblocks/backend-sdk 0.14.0 and Express 4.21.x. The SDK requires TypeScript for this tutorial.

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 the admin database. The withMongo connection URL below includes authSource=admin so authentication succeeds when connecting to the dev database. 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 identities and refreshtokens datastores, needed for registration, login, refresh-token, and logout flows. authService also 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. profileService also mounts avatar upload and avatar-normalization routes, which require a fileStorageDriver; do not send an avatar field or use avatar endpoints until you configure that driver. Follow/like routes are also outside this quickstart.

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 id field from the login response (Step 2) as identityId when 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: withMongo creates a new MongoDB client for each collection request. This is convenient for the quickstart, but production applications should create one MongoClient, reuse its collections across services, and close it during graceful shutdown.

🔗 Available Endpoints

Authentication Service (/auth)

  • POST /auth/register - Register a new identity
  • POST /auth/login - Login and get access token
  • POST /auth/logout - Logout and invalidate tokens (requires auth; bearer mode needs refreshToken in 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

  1. Register via /auth/register
  2. Login to get access and refresh tokens via /auth/login
  3. Use access token in Authorization: Bearer <token> header for protected endpoints

➡️ Next Steps