![Deno 2.2 Improves Dependency Management and Expands Node.js Compatibility](https://cdn.sanity.io/images/cgdhsj6q/production/97774ea8c88cc8f4bed2766c31994ebc38116948-1664x1366.png?w=400&fit=max&auto=format)
Security News
Deno 2.2 Improves Dependency Management and Expands Node.js Compatibility
Deno 2.2 enhances Node.js compatibility, improves dependency management, adds OpenTelemetry support, and expands linting and task automation for developers.
Bigriver is a highly-opinionated web framework on the top of Node.js, Redis and MongoDB. Unlike other JavaScripts frameworks, Bigriver is natively written on Typescript and applied to the modern patterns to ensure the type-safety of the framework.
Bigriver requires Node.js 14+, Redis and MongoDB. It's highly recommended to run Bigriver on Unix-like operating system with Nginx.
Just simply run
yarn add bigriver
or
npm install bigriver
to install the framework.
Bigriver uses a series of init functions like React's hook functions for its services.
Since Bigriver is built on the top of Fastify web framework, we can simply use "useApp" function to init the server.
const app = useApp({
routers: [
/* Your routers */
],
trustProxy: true,
})
const PORT = 8080
const ADDRESS = '127.0.0.1' // 0.0.0.0 if use docker
app.listen(PORT, ADDRESS, () => console.log(`Running on port ${PORT}`))
Simply use a mongodb uri to connect to it. Since Bigriver follows the best pattern we considered for our scenes, we don't provide any more options for MongoDB connection to avoid deprecations and performance decays.
await useMongoDB('mongodb://[URI]')
Redis connection options are the same as IORedis' connection options.
await useRedis({
host: '',
password: '',
port: 6379,
keyPrefix: '',
enableAutoPipelining: true,
reconnectOnError: () => 2,
})
Define a router in Bigriver is very easy. Let's look at an example of an user registration API to have a first impression on it.
const router = useRouter({
prefix: '/auth',
middlewares: [
/* Router-level middlewares for all routes */
],
})
router.post('/signup', {
schema: {
body: Type.Object({
countryCode: Type.Integer({ minimum: 1, maximum: 999 }),
mobile: Type.String({
pattern: StringPattern.Integer,
minLength: 6,
maxLength: 13,
}),
password: Type.String({ minLength: 6, maxLength: 32 }),
}),
},
handler: async req => {
const { countryCode, mobile, password } = req.body
// ...
return { success: true }
},
})
For the schema and handler part, we use the totally same logic as Fastify's. However, we introduced TypeBox to our framework to define the schema (validation). With TypeBox, we can easily define typed schemas and then we can use those type inference later in our request and response interfaces in the handler function.
Beside TypeBox, we also provide StringPattern
, StringType
and StringTo
three utility classes to help you define and convert string-related types. We also provide a LiteralType
class to help you deal with the condition of multiple literals (based on Json Schema's Union Type).
Let's take a look on an example of our code
const router = useRouter({
prefix: '/activities',
middlewares: [AuthGuard, RateLimit({ timeWindow: 60000, maxRequests: 20 })],
})
// Get activities
router.get('/', {
alternativePaths: ['/users/:userId/activities'],
middlewares: [
/* Route only middlewares */
],
schema: {
params: Type.Object({
userId: Type.Optional(StringType.ObjectID),
}),
querystring: Type.Object({
direction: LiteralType.anyOf(['from', 'to']),
type: Type.Optional(LiteralType.anyOf(['reaction', 'postComment'])),
limit: Type.Optional(StringType.Integer),
before: Type.Optional(StringType.DateTime),
after: Type.Optional(StringType.DateTime),
}),
},
handler: async req => {
const { user } = req.state
const { direction, type } = req.query
const limit = StringTo.Number(req.query.limit)
const before = StringTo.Date(req.query.before)
const after = StringTo.Date(req.query.after)
if (!req.params.userId && !user.permissions.includes('manageEntity')) {
throw new Forbidden()
}
return []
},
})
In this part of code, you can also see that there is an alternativePath
option. It's used to create a alternative path for a route (without the prefix). It's helpful when you want a handler to handle two slightly different routes without repeating your codes.
You can also find that we use middlewares here. When you set middlewares in useRouter
, these middlewares will be applied to all routes under the router. When you set middlewares for a route itself, it will only be applied for itself.
It's highly recommended to also install http-errors
(a Node.js module) to throw user-friendly HTTP errors, such as 403 Forbidden in this part of code.
We use the "AuthGuard" as an example to show you how to write a middleware. Writing a middleware is just like the way you write your handlers for routers.
export const AuthGuard: Middleware = async req => {
if (!req.headers.authorization) {
throw new Unauthorized()
}
const token = req.headers.authorization.substr(7)
const result = await AuthService.verifyToken(token)
req.state.user = result
}
By default, we don't have a key called "user" under our request state. Therefore, it's important to create a type file for typescript to recoginize it. You may simply create a type.d.ts in your source code directory.
declare module 'fastify' {
interface RequestState {
user: {
_id: string
permissions: Permission[]
}
}
}
By modify the request state, you will be allowed to get them in your route handler like this:
router.get('/', {
schema: {},
handler: async req => {
const { user } = req.state
// ...
},
})
In typical Web frameworks, there is usually a concept called ORM (Object-relational Mapping) or ODM (Object-document Mapping). However, the implementation of ORMs or ODMs usually brings performance decays and makes everything complicated. In Bigriver, we don't use any relational mapping. Instead, we use TypeScript's language features to implement a Virtual Model
. Let's quickly look at an example:
export interface Place extends BaseModel, Timestamps {
name: string
description: string
location: GeoJsonPoint
owner: Ref<User>
}
export const PlaceModel = useModel<Place>({
collection: 'place',
timestamps: true,
sync: true,
indexes: [
[{ owner: 1 }],
[{ 'location': '2dsphere' }],
],
})
const asyncfunc = async () => {
await PlaceModel.create({
name: 'New Place',
description: 'Something',
location: {
type: 'Point',
coordinates: [0, 0],
},
owner: /* An user model's ObjectID */,
})
const places = await PlaceModel.find({}).toArray()
await PlaceModel.populate(places, [
{
model: UserModel,
path: 'owner',
select: ['nickname', 'avatarUrl'],
},
])
console.log(places)
}
asyncfunc()
Basically, the query functions are just the same syntax as Node.js MongoDB driver (Because we built Bigriver on the top of it). Our useModel
function will create a virtual model for you. The virtual model will automatically create the indexes and handle timestamps for your model. In this case, PlaceModel.populate
will help you to join references based on BSON ObjectIDs. The usage of this populate
function is similar to the one in Mongoose.
If you have any question about Bigriver, you can just open an issue, create a pull request, or send an email to opensource@topos.company for further discussions.
FAQs
A fast well-typed back-end prototyping framework
The npm package bigriver receives a total of 2 weekly downloads. As such, bigriver popularity was classified as not popular.
We found that bigriver demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Deno 2.2 enhances Node.js compatibility, improves dependency management, adds OpenTelemetry support, and expands linting and task automation for developers.
Security News
React's CRA deprecation announcement sparked community criticism over framework recommendations, leading to quick updates acknowledging build tools like Vite as valid alternatives.
Security News
Ransomware payment rates hit an all-time low in 2024 as law enforcement crackdowns, stronger defenses, and shifting policies make attacks riskier and less profitable.