Note
This documentation reflects the beta version of NodePlace. The current alpha version is a pre-release, aimed at gathering feedback from the community to shape the final product and address key developer pain points.
Typescript support is only available in es modules. however, that does not mean that the code will break in commonjs modules, only that you will have to turn of noImplicitAny in tsConfig and also other fields that may cause the ts compiler to complain.
This alpha version is not intended for serious projects but to try out and explore. Please report any issues or bugs you encounter on our Discord server. Your feedback is invaluable. Thank you in advance for helping make NodePlace better!
Introduction
What is NodePlace.js?
NodePlace is a lightweight, zero-dependency Node.js framework designed for building fast, scalable server-side applications. With a strong focus on simplicity and performance, NodePlace is ideal for creating APIs, microservices, or server-driven applications. Its intuitive design makes it a perfect drop-in replacement for Express.js, sharing familiar syntax and structure while introducing additional features tailored for modern development.
Pre-Requisites
To get the most out of NodePlace, itβs recommended that you have a foundational understanding of Node.js. While not strictly required, prior experience with Express.js can accelerate the learning curve, as this framework have a similar syntax and structure.
Features
Core Features
feature | Description |
---|---|
Routing | Intuitive system to define how HTTP requests are handled based on URL paths and methods. |
Middleware | Handles request/response interception, enabling flexible functionality and modular design. |
Error Handling | Provides robust error-catching and custom error-handler support for consistent responses. |
Static File Serving | Efficiently serves static files with options for caching, custom headers, and file extension fallbacks. |
Additional Features
feature | Description |
---|---|
Zero dependency | Lightweight and simple, with no external dependencies to minimize peer dependency issues. |
Flexible Event System | Easily manage custom events to streamline workflows for real-time and asynchronous tasks. |
Seamless Integration with Node.js | Designed to integrate effortlessly with the Node.js ecosystem. |
Performance-Optimized Architecture | Built with high performance and scalability in mind. |
Comprehensive API Support | Equipped with modern HTTP utilities for JSON parsing, cookie management, and more. |
Installation
Installation via NPM
To start using NodePlace, install it using your preferred package manager. We recommend using npm or yarn for simplicity:
npm install nodeplace
or if you use yarn:
yarn add nodeplace
Installation via NodePlace CLI
For users who want to get started with NodePlace alongside an existing template, either from NodePlace itself or the community, the NodePlace CLI provides a seamless solution.
Install the CLI globally using npm:
ynpm install -g @nodeplace/cli
After installation, you can create a new NodePlace application by running:
npx create-nodeplace-app
Follow the prompts to select a template from the list.
Alternatively, you can specify a template directly:
npx create-nodeplace-app --template@template-name
Note: The CLI is in alpha; feedback is appreciated to improve its functionality.
Requirements
- Node.js 16 or higher: NodePlace leverages modern JavaScript features, so ensure your environment is up to date.
- Package Manager: Ensure you have npm or yarn installed to manage dependencies effectively.
Hello World
Getting started with NodePlace is simple. Here's a quick example to set up a basic server:
import nodeplace from 'nodeplace'
const app = nodeplace()
app.get('/', (req, res) => {
res.send('Hello, World!')
})
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000')
})
Explanation
- Import the Framework:
- Create an Application Instance:
- Define a Route:
- Start the Server:
Use the nodeplace package to access all the core functionalities.
nodeplace() initializes a new app, similar to other Node.js frameworks.
The app.get() method specifies an HTTP GET handler for the root URL (/).
The app.listen() method starts the server on port 3000 and listens for incoming requests.
"The req (request) and res (response) objects in NodePlace are built on the native Node.js objects, with added methods and properties to simplify common tasks. You can still use native methods like req.pipe() or req.on('data', callback) seamlessly, while benefiting from the extended capabilities provided by NodePlace."
Running the Server
- Create a file named server.js and paste the code above into it.
- Run the app with:
node server.js
Visit http://localhost:3000 in your browser or API testing tool (e.g., Postman or Insomnia). You should see the text "Hello, World!".
Routing
Routing determines how your application responds to client requests for specific endpoints, defined by a URI (path) and an HTTP method (GET, POST, etc.).
In NodePlace, each route can have one or more handler functions executed when the route matches an incoming request.
Hereβs the basic structure of a route:
app.METHOD(PATH, HANDLER)
Where:
- app is an instance of express.
- METHOD is an HTTP request method, in lowercase.
- PATH is a path on the server.
- HANDLER is the function executed when the route is matched.
For example:
app.get('/', (req, res) => {
res.send('Hello, World!')
})
The req (request) and res (response) objects have enhanced properties, allowing seamless access to request data and sending responses.
This tutorial assumes that an instance of express named app is created and the server is running. If you are not familiar with creating an app and starting it, see the Hello world example.
Route methods
A route method corresponds to an HTTP method. In NodePlace, you can define routes for various HTTP methods like get, post, put, etc.
// Define a GET route
app.get('/', (req, res) => {
res.send('GET request to the homepage')
})
// Define a POST route
app.post('/', (req, res) => {
res.send('POST request to the homepage')
})
NodePlace supports all standard HTTP methods. For instance:
Respond to a PUT request:
app.put('/user', (req, res) => {
res.send('Got a PUT request at /user')
})
Respond to a PATCH request:
app.patch('/user', (req, res) => {
res.send('Got a PATCH request at /user')
})
Respond to a DELETE request to the /user route:
app.delete('/user', (req, res) => {
res.send('Got a DELETE request at /user')
})
Route Parameters
Route parameters are dynamic segments of a URL. They allow you to capture values in the URL path and make them accessible in the req.params object.
Example:
// Define a route with parameters
app.get('/users/:userId/books/:bookId', (req, res) => {
res.send(req.params)
})
// Request URL: /users/34/books/8989
// req.params: { "userId": "34", "bookId": "8989" }
Route Handlers
You can define multiple handlers for a single route to process requests in stages:
Single handler function:
app.get('/example', (req, res) => {
res.send('Hello from Example!')
})
Multiple handlers:
app.get('/example', (req, res, next) => {
console.log('Handler 1 executed')
next()
}, (req, res) => {
res.send('Handler 2 executed')
})
Array of handlers:
const middleware1 = (req, res, next) => { console.log('Middleware 1'); next() }
const middleware2 = (req, res, next) => { console.log('Middleware 2'); next() }
app.get('/example', [middleware1, middleware2, (req, res) => {
res.send('Hello from Example!')
}])
NodePlace Router
The Router module in NodePlace lets you modularize routes and middleware, treating them as mini-apps. This is helpful for organizing related routes.
Example:
// birds.js
import nodeplace from 'nodeplace'
const router = nodeplace.Router()
// Middleware specific to this router
router.use((req, res, next) => {
console.log('Time:', Date.now())
next()
})
// Define routes
router.get('/', (req, res) => {
res.send('Birds home page')
})
router.get('/about', (req, res) => {
res.send('About birds')
})
export default router
Mount the router in your main app:
import birds from './birds'
app.use('/birds', birds)
The app can now handle requests to /birds and /birds/about.
Using Middleware
Middleware in NodePlace is a key part of the request-response lifecycle. It allows you to execute code, make changes to request and response objects, end the request-response cycle, or pass control to the next middleware function in the stack. Middleware functions typically receive three parameters: req, res, and next.
Middleware functions can:
- Execute any code.
- Modify req or res objects.
- End the request-response cycle.
- Call the next middleware in the stack using next().
Types of Middleware
Application-Level Middleware: Application-level middleware applies to the entire application and can be mounted using app.use() or app.METHOD(). Here's an example:
Global Middleware (No Mount Path)
This middleware runs for every incoming request:
const nodeplace = require('nodeplace')
const app = nodeplace()
app.use((req, res, next) => {
console.log('Time:', Date.now())
next()
})
Middleware with a Mount Path
This middleware runs for requests to /user/:id:
app.use('/user/:id', (req, res, next) => {
console.log('Request Type:', req.method)
next()
})
Route-Level Middleware
Middleware can be tied to specific HTTP methods and routes:
app.get('/user/:id', (req, res, next) => {
console.log('User ID:', req.params.id)
next()
}, (req, res) => {
res.send('User Info')
})
Middleware Sub-Stacks
You can group multiple middleware functions to create a middleware stack:
app.use('/user/:id', [
(req, res, next) => {
console.log('Request URL:', req.originalUrl)
next()
},
(req, res, next) => {
console.log('Request Type:', req.method)
next()
}
])
Writing Middleware
Creating custom middleware is straightforward in NodePlace. Middleware is simply a function that accepts req, res, and next parameters. Here's how you can write middleware.
This middleware logs request information:
const logRequest = (req, res, next) => {
console.log(`Request URL: ${req.url}`)
console.log(`Request Method: ${req.method}`)
next() // Pass control to the next middleware
}
app.use(logRequest)
Middleware that Ends the Response
If the middleware completes the request-response cycle, it doesnβt need to call next():
const blockAccess = (req, res) => {
res.status(403).send('Access Denied')
}
app.use(blockAccess)
Middleware with Parameters
Middleware can be dynamic by accepting parameters:
const setCustomHeader = (headerName, headerValue) => (req, res, next) => {
res.setHeader(headerName, headerValue)
next()
}
app.use(setCustomHeader('X-Custom-Header', 'MyValue'))
Error-Handling Middleware
Error-handling middleware has four parameters: (err, req, res, next):
const errorHandler = (err, req, res, next) => {
console.error(err.stack)
res.status(500).send('Something went wrong!')
}
app.use(errorHandler)
Middleware in NodePlace is versatile and allows you to build modular, reusable logic for handling requests. By understanding how to use and write middleware, you can easily create powerful applications that handle complex workflows with ease.
Error Handling
Error handling in NodePlace ensures your application gracefully handles unexpected issues, both synchronously and asynchronously. NodePlace provides a built-in error handler, so you don't need to start from scratch. However, customizing error handling allows for tailored responses and better debugging.
Catching Errors
Errors in your application can occur during route handling or middleware execution. NodePlace offers robust mechanisms to catch both synchronous and asynchronous errors.
Synchronous Errors
Synchronous code errors are automatically caught by NodePlace without additional configuration. For instance:
app.get('/', (req, res) => {
throw new Error('Something went wrong!') // Automatically handled.
})
Asynchronous Errors
For asynchronous errors, explicitly pass the error to the next() function:
import fs from 'fs'
app.get('/', (req, res, next) => {
fs.readFile('/non-existent-file', (err, data) => {
if (err) {
next(err) // Passes the error to the handler.
} else {
res.send(data)
}
})
})
In modern JavaScript, you can also rely on async/await for cleaner error handling:
app.get('/user/:id', async (req, res, next) => {
try {
const user = await getUserById(req.params.id)
res.send(user)
} catch (error) {
next(error) // Automatically passes the error to NodePlace's error handler.
}
})
Promises and Errors
Promises make error handling easier by catching both synchronous and asynchronous errors:
app.get('/', (req, res, next) => {
Promise.resolve().then(() => {
throw new Error('Promise error!')
}).catch(next) // Automatically handles the error.
})
Common Patterns
You can chain middleware functions to simplify error handling in complex workflows:
app.get('/', [
(req, res, next) => {
fs.writeFile('/path', 'data', next) // Passes errors to the next function.
},
(req, res) => {
res.send('Operation succeeded!')
}
])
Even in asynchronous code, you can use try...catch or promise chains to ensure NodePlace receives the error:
app.get('/', (req, res, next) => {
setTimeout(() => {
try {
throw new Error('Timeout error!')
} catch (err) {
next(err)
}
}, 100)
})
Writing error handlers
Error-handling middleware functions in NodePlace follow the same structure as regular middleware but must include four arguments: (err, req, res, next).
Basic Error Handler
Here's a simple error-handling middleware:
app.use((err, req, res, next) => {
console.error(err.stack) // Logs the error.
res.status(500).send('Something went wrong!')
})
Defining Error Handlers
Define error-handling middleware after all other middleware and routes:
app.use(bodyParser.json())
app.use(methodOverride())
// Error handler
app.use((err, req, res, next) => {
res.status(500).json({ error: err.message })
})
Custom Error Handlers
Custom error handlers allow tailored responses:
XHR Request Error:
const clientErrorHandler = (err, req, res, next) => {
if (req.xhr) {
res.status(500).send({ error: 'XHR failed!' })
} else {
next(err)
}
}
app.use(clientErrorHandler)
Logging Errors:
const logErrors = (err, req, res, next) => {
console.error(err.stack)
next(err)
}
app.use(logErrors)
Catch-All Handler:
const errorHandler = (err, req, res) => {
res.status(500).render('error', { error: err })
}
app.use(errorHandler)
Skipping Handlers
Skip specific route handlers by passing 'route' to next():
app.get('/restricted', (req, res, next) => {
if (!req.user.isAuthenticated) {
next('route') // Skips to the next route handler.
} else {
res.send('Welcome!')
}
})
Effective error handling is crucial for robust applications. NodePlace simplifies this with built-in support for synchronous and asynchronous errors, along with customizable error-handling middleware. Whether logging errors, handling API-specific issues, or providing user-friendly messages, NodePlace empowers you to manage errors gracefully.
Application Settings
NodePlace allows you to manage application settings using the app.settings method:
app.settings({poweredBy: false });
You can also enable or disable individual settings:
app.enable('poweredBy');
app.disable('poweredBy');