All integrations

Node.js Integration

Express, Fastify, Hapi, and more

Turalogin integrates seamlessly with any Node.js backend. Use a single service file to call Turalogin from your login endpoint. The user receives a magic link email that redirects to your callback URL. Works the same whether you're running Express, Fastify, Hapi, or a custom HTTP server.

  • Drop-in for any Node backend
  • Magic link flow with customizable callback URL
  • Works with Express, Fastify, Hapi, Koa, and more
  • No SDK required - just fetch or axios
  • CommonJS and ESM support

Implementation Examples

1. Environment Configuration

Set up your environment variables for local development and production.

.env
1# Your Turalogin API key from the dashboard
2TURALOGIN_API_KEY=tl_live_xxxxxxxxxxxxx
3
4# The URL where magic links will redirect to
5# Development:
6APP_LOGIN_VALIDATION_URL=http://localhost:3000/auth/callback
7
8# Production (update for your domain):
9# APP_LOGIN_VALIDATION_URL=https://myapp.com/auth/callback
10
11# Session secret for express-session
12SESSION_SECRET=your-session-secret-here

2. Start Authentication (Express)

Create a route to initiate authentication. This sends a magic link to the user's email.

routes/auth.js
1const express = require('express');
2const router = express.Router();
3
4const TURALOGIN_API_KEY = process.env.TURALOGIN_API_KEY;
5const TURALOGIN_API_URL = 'https://api.turalogin.com/api/v1';
6const VALIDATION_URL = process.env.APP_LOGIN_VALIDATION_URL;
7
8router.post('/start', async (req, res) => {
9 try {
10 const { email } = req.body;
11
12 if (!email) {
13 return res.status(400).json({ error: 'Email is required' });
14 }
15
16 const response = await fetch(`${TURALOGIN_API_URL}/auth/start`, {
17 method: 'POST',
18 headers: {
19 'Authorization': `Bearer ${TURALOGIN_API_KEY}`,
20 'Content-Type': 'application/json',
21 },
22 body: JSON.stringify({
23 email,
24 validationUrl: VALIDATION_URL // Where the magic link redirects to
25 }),
26 });
27
28 const data = await response.json();
29
30 if (!response.ok) {
31 return res.status(response.status).json(data);
32 }
33
34 res.json({
35 success: true,
36 message: 'Check your email for the login link'
37 });
38
39 } catch (error) {
40 console.error('Auth start error:', error);
41 res.status(500).json({ error: 'Internal server error' });
42 }
43});
44
45module.exports = router;

3. Handle Magic Link Callback

Create a callback route that receives the token from the magic link and verifies it.

routes/auth.js
1// GET /auth/callback?token=xxx - Handle magic link redirect
2router.get('/callback', async (req, res) => {
3 try {
4 const { token } = req.query;
5
6 if (!token) {
7 return res.redirect('/login?error=invalid_link');
8 }
9
10 // Verify the token with Turalogin
11 const response = await fetch(`${TURALOGIN_API_URL}/auth/verify`, {
12 method: 'POST',
13 headers: {
14 'Authorization': `Bearer ${TURALOGIN_API_KEY}`,
15 'Content-Type': 'application/json',
16 },
17 body: JSON.stringify({ sessionId: token }),
18 });
19
20 const data = await response.json();
21
22 if (!response.ok) {
23 return res.redirect('/login?error=verification_failed');
24 }
25
26 const { token: jwt, user } = data;
27
28 // Create your own session
29 req.session.userId = user.id;
30 req.session.email = user.email;
31 req.session.turaloginToken = jwt;
32
33 // Redirect to dashboard
34 res.redirect('/dashboard');
35
36 } catch (error) {
37 console.error('Auth callback error:', error);
38 res.redirect('/login?error=server_error');
39 }
40});

4. Fastify Alternative

The same pattern works with Fastify's request/reply API.

routes/auth.ts
1import Fastify from 'fastify';
2import fastifySession from '@fastify/session';
3import fastifyCookie from '@fastify/cookie';
4
5const fastify = Fastify();
6
7const TURALOGIN_API_KEY = process.env.TURALOGIN_API_KEY!;
8const TURALOGIN_API_URL = 'https://api.turalogin.com/api/v1';
9const VALIDATION_URL = process.env.APP_LOGIN_VALIDATION_URL!;
10
11// Register plugins
12fastify.register(fastifyCookie);
13fastify.register(fastifySession, {
14 secret: process.env.SESSION_SECRET!,
15 cookie: { secure: process.env.NODE_ENV === 'production' },
16});
17
18// Start auth - send magic link
19fastify.post('/auth/start', async (request, reply) => {
20 const { email } = request.body as { email: string };
21
22 const response = await fetch(`${TURALOGIN_API_URL}/auth/start`, {
23 method: 'POST',
24 headers: {
25 'Authorization': `Bearer ${TURALOGIN_API_KEY}`,
26 'Content-Type': 'application/json',
27 },
28 body: JSON.stringify({
29 email,
30 validationUrl: VALIDATION_URL
31 }),
32 });
33
34 if (!response.ok) {
35 const error = await response.json();
36 return reply.status(response.status).send(error);
37 }
38
39 return { success: true, message: 'Check your email for the login link' };
40});
41
42// Callback - verify magic link token
43fastify.get('/auth/callback', async (request, reply) => {
44 const { token } = request.query as { token: string };
45
46 const response = await fetch(`${TURALOGIN_API_URL}/auth/verify`, {
47 method: 'POST',
48 headers: {
49 'Authorization': `Bearer ${TURALOGIN_API_KEY}`,
50 'Content-Type': 'application/json',
51 },
52 body: JSON.stringify({ sessionId: token }),
53 });
54
55 if (!response.ok) {
56 return reply.redirect('/login?error=verification_failed');
57 }
58
59 const data = await response.json();
60 request.session.set('user', data.user);
61
62 return reply.redirect('/dashboard');
63});

5. Turalogin Service Module

Extract Turalogin calls into a reusable service module for cleaner code.

services/turalogin.js
1class TuraloginService {
2 constructor(apiKey, validationUrl) {
3 this.apiKey = apiKey;
4 this.validationUrl = validationUrl;
5 this.baseUrl = 'https://api.turalogin.com/api/v1';
6 }
7
8 async request(endpoint, body) {
9 const response = await fetch(`${this.baseUrl}${endpoint}`, {
10 method: 'POST',
11 headers: {
12 'Authorization': `Bearer ${this.apiKey}`,
13 'Content-Type': 'application/json',
14 },
15 body: JSON.stringify(body),
16 });
17
18 const data = await response.json();
19
20 if (!response.ok) {
21 const error = new Error(data.error || 'Turalogin API error');
22 error.status = response.status;
23 error.code = data.code;
24 throw error;
25 }
26
27 return data;
28 }
29
30 async startAuth(email) {
31 return this.request('/auth/start', {
32 email,
33 validationUrl: this.validationUrl
34 });
35 }
36
37 async verifyToken(sessionId) {
38 return this.request('/auth/verify', { sessionId });
39 }
40}
41
42module.exports = new TuraloginService(
43 process.env.TURALOGIN_API_KEY,
44 process.env.APP_LOGIN_VALIDATION_URL
45);

6. Error Handling Middleware

Centralized error handling for Turalogin-related errors.

middleware/errorHandler.js
1function turaloginErrorHandler(err, req, res, next) {
2 // Handle Turalogin-specific errors
3 if (err.code) {
4 switch (err.code) {
5 case 'INVALID_EMAIL':
6 return res.status(400).json({
7 error: 'Please provide a valid email address',
8 code: err.code,
9 });
10
11 case 'SESSION_EXPIRED':
12 return res.status(400).json({
13 error: 'Login link has expired. Please try again.',
14 code: err.code,
15 });
16
17 case 'INVALID_TOKEN':
18 return res.status(400).json({
19 error: 'Invalid login link. Please request a new one.',
20 code: err.code,
21 });
22
23 case 'RATE_LIMITED':
24 return res.status(429).json({
25 error: 'Too many attempts. Please wait a moment.',
26 code: err.code,
27 retryAfter: err.retryAfter,
28 });
29
30 default:
31 console.error('Turalogin error:', err);
32 return res.status(err.status || 500).json({
33 error: 'Authentication error. Please try again.',
34 code: err.code,
35 });
36 }
37 }
38
39 // Pass to default error handler
40 next(err);
41}
42
43module.exports = turaloginErrorHandler;
44
45// Usage in app.js:
46// const errorHandler = require('./middleware/errorHandler');
47// app.use(errorHandler);

Complete Express Application

A complete Express application with authentication routes, session management, and protected endpoints.

app.js
1const express = require('express');
2const session = require('express-session');
3const cors = require('cors');
4const turalogin = require('./services/turalogin');
5const errorHandler = require('./middleware/errorHandler');
6
7const app = express();
8
9// Middleware
10app.use(cors({ origin: process.env.FRONTEND_URL, credentials: true }));
11app.use(express.json());
12app.use(session({
13 secret: process.env.SESSION_SECRET,
14 resave: false,
15 saveUninitialized: false,
16 cookie: {
17 secure: process.env.NODE_ENV === 'production',
18 httpOnly: true,
19 maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days
20 },
21}));
22
23// Start auth - send magic link email
24app.post('/auth/start', async (req, res, next) => {
25 try {
26 const { email } = req.body;
27 await turalogin.startAuth(email);
28 res.json({ success: true, message: 'Check your email for the login link' });
29 } catch (err) {
30 next(err);
31 }
32});
33
34// Callback - handle magic link redirect
35app.get('/auth/callback', async (req, res, next) => {
36 try {
37 const { token } = req.query;
38
39 if (!token) {
40 return res.redirect('/login?error=invalid_link');
41 }
42
43 const { token: jwt, user } = await turalogin.verifyToken(token);
44
45 // Store in session
46 req.session.userId = user.id;
47 req.session.email = user.email;
48 req.session.token = jwt;
49
50 res.redirect('/dashboard');
51 } catch (err) {
52 console.error('Auth callback error:', err);
53 res.redirect('/login?error=verification_failed');
54 }
55});
56
57app.post('/auth/logout', (req, res) => {
58 req.session.destroy((err) => {
59 if (err) {
60 return res.status(500).json({ error: 'Logout failed' });
61 }
62 res.clearCookie('connect.sid');
63 res.json({ success: true });
64 });
65});
66
67// Auth middleware
68function requireAuth(req, res, next) {
69 if (!req.session.userId) {
70 return res.status(401).json({ error: 'Authentication required' });
71 }
72 next();
73}
74
75// Protected routes
76app.get('/api/me', requireAuth, (req, res) => {
77 res.json({
78 id: req.session.userId,
79 email: req.session.email,
80 });
81});
82
83app.get('/api/dashboard', requireAuth, (req, res) => {
84 res.json({ message: 'Welcome to the dashboard!' });
85});
86
87// Error handling
88app.use(errorHandler);
89
90// Start server
91const PORT = process.env.PORT || 3000;
92app.listen(PORT, () => {
93 console.log(`Server running on port ${PORT}`);
94});

Ready to integrate?

Create your Turalogin account and get your API key in minutes.