The Nile-JS SDK includes generated routes for all its operations.
These routes are used by the SDK methods to proxy requests to nile-auth, as well as directly from the React hooks and components in @niledatabase/react.
import"dotenv/config";import express from"express";import{ Nile }from"@niledatabase/server";import{ NileExpressHandler }from"@niledatabase/server/express";conststartServer=async()=>{try{const app =express();const nile =awaitNile();// This is where the route handlers are importedconst{ paths, handler }=awaitNileExpressHandler(nile); app.use(express.json()); app.use(express.urlencoded({ extended:true}));// This is where the routes are exposed in your express app app.get(paths.get, handler); app.post(paths.post, handler); app.put(paths.put, handler); app.delete(paths.delete, handler);// Your own routes go hereconstPORT= process.env.PORT||3040; app.listen(PORT,()=>{console.log(`Server is running on port ${PORT}`);});}catch(error){console.error("Error starting server:", error); process.exit(1);}};startServer();
You typically don’t need to use the routes directly.
The SDK methods and react/web components use the routes under the hood to communicate with nile-auth.
The generated routes are available via nile.api.paths. For reference, here are the paths for the default routes:
exportconst appRoutes =(prefix ='/api'): Routes =>({SIGNIN:`${prefix}/auth/signin`,PROVIDERS:`${prefix}/auth/providers`,SESSION:`${prefix}/auth/session`,CSRF:`${prefix}/auth/csrf`,CALLBACK:`${prefix}/auth/callback`,// this path has a route per enabled provider (e.g. google, github, etc.)SIGNOUT:`${prefix}/auth/signout`,ERROR:`${prefix}/auth/error`,VERIFY_REQUEST:`${prefix}/auth/verify-request`,PASSWORD_RESET:`${prefix}/auth/reset-password`,ME:`${prefix}/me`,USERS:`${prefix}/users`,TENANTS:`${prefix}/tenants`,TENANT:`${prefix}/tenants/{tenantId}`,TENANT_USER:`${prefix}/tenants/{tenantId}/users/{userId}`,TENANT_USERS:`${prefix}/tenants/{tenantId}/users`,SIGNUP:`${prefix}/signup`,LOG:`${prefix}/_log`,});
A case where you might want to use the routes directly is when these routes are exposed as a REST API of your application backend.
For example, if you use the @niledatabase/server/express package, the routes are exposed in the app object and
while you may have a frontend that uses @niledatabase/react to call these routes from the application UI,
you may want to also use them as a REST API for another backend or external service.
In this case, you need to use the routes directly. The key is to:
Include both the session cookie and the CRSF cookie in the request headers.
Include the CSRF token in the request body.
Here are a few examples of how to call the routes directly:
# This gets the CSRF token from an API exposed by your application, and also saves the cookies to a filecsrf_token=$(curl-s-X GET "http://localhost:3040/api/auth/csrf"-c"csrf_cookies.txt"| jq -r'.csrfToken')
Sometimes you might want to intercept or override the routes used by the SDK in order to inject your own logic.
For example, adding your own logging or metrics, adding debugging information, or perhaps injecting your own cookies during login.
There are three ways to override routes:
Route wrappers: Wrap the route in your own logic. This can be done in the routes file, and is useful for minor modifications or debugging.
Route overrides: Override the route for a specific operation.
In the examples below, we’ll use route wrappers to log the headers before every request and the body if it’s a POST request.
We’ll also log the status code of the response.
// app/api/[...nile]/route.tsimport{ handlers }from"./nile";// Middleware function to log request and response detailsconstlogRequestAndResponseDetails=(handler)=>async(req, res)=>{// Log the request method and URLconsole.log(`Request Method: ${req.method}, Request URL: ${req.url}`);// Log the request headersconsole.log('Request Headers:', req.headers);// Clone the request to safely read the bodyconst clonedReq = req.clone();// Log the request body if it's a POST or PUT requestif(req.method ==='POST'){const body =await clonedReq.text();console.log('Request Body:', body);}// Call the original handler and return its resultconst result =awaithandler(req, res);// Log the response status after the handler has executedconsole.log('Result Status:', result.status);return result;};// Wrap each handler with the logging middlewareexportconstPOST=logRequestAndResponseDetails(handlers.POST);exportconstGET=logRequestAndResponseDetails(handlers.GET);exportconstDELETE=logRequestAndResponseDetails(handlers.DELETE);exportconstPUT=logRequestAndResponseDetails(handlers.PUT);
In the examples below, we’ll add a new route that will override the default route for \auth\google\callback with custom logic.
// app/api/auth/google/callback/route.tsimport{ NextRequest }from"next/server";import{ nile, handlers }from"../../../[...nile]/nile";import{ registerTenants }from"@/lib/TenantRegistration";exportasyncfunctionGET(req: NextRequest){const postHandled =await handlers.GET(req);// call the original routeif(postHandled){const setCookie = postHandled.headers.getSetCookie();const hasSession = setCookie.filter((c)=> c.includes("nile.session-token"));if(hasSession){// if login was successful, register the tenants nile.api.headers =newHeaders({ cookie: hasSession.toString()});const me =await nile.api.users.me();if("id"in me){// custom logic is hereawaitregisterTenants(me.id);}}}// return the original response from the routereturn postHandled;}