Merge multiple Prisma schema files, model inheritance, resolving name conflicts and timings reports, all in a simple tool.

Overview

Prisma Util



What is Prisma Util?   •   How to use?   •   The configuration file   •   Support

What is Prisma Util?

Prisma Util is an easy to use tool that merges multiple Prisma schema files, allows extending of models, resolves naming conflicts and provides easy access to Prisma commands and timing reports. It's mostly a plug-and-play replacement, with an easy confirguration file.

Requirements:

How to Use?

npx prisma-util [options]

The Prisma Util is built on top of Prisma, and as such all arguments and commands are the same as the ones from the official documentation. The only additional parameter available is --config and it allows you to change the path of the config file (default: prisma-util.config.json).

The configuration file

Table of Contents

  1. Concepts
    1. Running Commands
    2. Timings & Reports
    3. Model & Column Representation
    4. Resolving Conflicts
    5. Automatic Mapper & Experimental Features
    6. Debugging and Common Errors
    7. Polyfills
  2. Configuration
    1. Include Files
    2. Excluding Models
    3. Base Schema
    4. Cross-file Relations
    5. Defining Cross-file Relations
    6. Extending Models & Inheritance

Concepts

Running Commands

Prisma Util is mostly a plug-and-play replacement for Prisma. To run commands, use the following command:

npx prisma-util [options]

The options are indicated in the official documentation.

If you don't want to use Prisma Util as a replacement for Prisma, you can generate a schema using:

npx prisma-util schema

Additionally, you can add --path [path] to change the path for the generated schema. The default path is ./node_modules/.bin/generated-schema.prisma.

Timings & Reports

After you execute a command, Prisma Util will always end the output stream with how long it took to run the underlaying command. The last message will always be, regardless of errors, where X and Y are variable numbers:

PRISMA UTIL  Command executed in Xs and Yms.

Model & Column Representation

To differentiate between different files, Prisma Util uses this structure to represent files, models and columns:

[pathToFile]:[modelName].[columnName]

This would represent, in prisma schema, a file with the path [pathToFile] and content:

model [modelName] {
    ...
    [columnName] [columnType] [columnConstraints]
}

Resolving Conflicts

A name conflict (or conflict for short) happens when 2 models from different files share the same name. Prisma Util will allow you to choose an action for either one of them, and the conflicted files will always be shown in pairs.

This is how a conflict message generally looks like:

CONFLICT  Conflicts detected, please answer the questions below.

CONFLICT  Review your schema, then choose an option to solve the conflict.

Two models have the same name, please select an action.
[filePath1]:[modelName1] and [filePath2]:[modelName2]

Your choice: (Use arrow keys)
❯ Skip [filePath1]:[modelName1]
  Skip [filePath2]:[modelName2]
  Rename [filePath1]:[modelName1]
  Rename [filePath2]:[modelName2]

You can navigate this menu using arrow keys, then click enter to lock onto an option. After you select an option, depending on your choice, a separate prompt might appear. If you chose Skip, you won't get another prompt for this conflict. If you chose Rename, you'll receive an imput like this where you can type the new name. As usual, clicking Enter will submit your new input:

CONFLICT  What is the new name for [fileName]:[modelName]?

There is also one more special conflict, and that is generated by the Automatic Mapper when it can't process a conflict automatically. This occurs when a model from the relations map in your configuration has the same name as a model from another file. You'll be asked to deal with the model from the other file, as the relations map takes priority.

EXPERIMENTAL  The Automatic Mapper can't process a conflict automatically.

CONFLICT  Review your schema, then choose an option to solve the conflict.

[fileName1]:[modelName1] is referenced in your configuration file as the replacement for another model.   
However, [fileName2]:[modelName2] has the same model name as the generated one would.
Please choose one of the options below.

Your choice: (Use arrow keys)
❯ Skip [fileName2]:[modelName2]
  Rename [fileName2]:[modelName2]

Choosing Skip will omit the model from the other file and choosing Rename will show the same input as before:

CONFLICT  What is the new name for [fileName]:[modelName]?

Automatic Mapper & Experimental Features

Some features are marked as Experimental and will display an EXPERIMENTAL tag in front of all of their messages. These features need to be enabled manually in the configuration file.

Experimental features are not features that don't work or are prone to breaking, but rather features that alterate the way Prisma Util processes commands.

Debugging and Common Errors

Prisma Util will always show a message of what command it's running to help make debugging easier for you. An example of such message is:

PRISMA UTIL  prisma init --datasource-provider postgresql 

Some common errors that you might encounter while using Prisma Util are:

Cross-file relations are not enabled in [configurationfile].

This error message appears when one of your models uses a relation from a model that appears in another file.

Fix: Enable crossFileRelations in your configuration file.

Error validating field [field] in model [model]: The relation field [anotherfield] on Model [model] is missing an opposite relation field on the model [model]. Either run prisma format or add it manually.

Prisma Util didn't resolve your relations because you didn't specify them in the configuration file.

Fix: Enable crossFileRelations in your configuration file and fill in the relations object.

Polyfills

We use the term polyfill to describe a model that is only added to a file to satisfy an IDE extension for the purpose of creating relations. If you used a Visual Studio Code extension, you might be aware of the errors you get when you use an undefined model as a relation. To fix that, we've implemented polyfills using the Automatic Mapper.

To create a polyfill, just make add an almost empty model:

schema.prisma

model Post {
    id Int @Id @default(autoincrement())
    authorId Int
    author User @relation(fields: [authorId], references: [id])
}

model User {
    id Int @Id @default(autoincrement())
    name String @unique
    posts Post[]
}

test.prisma

model Reply {
    id Int @Id @default(autoincrement())
    title String @unique
    body String
}

Configuration file

{
    "relations": {
        "schema.prisma:User.posts": "test.prisma:Reply"
    }
}

In this example, Post is the polyfill model and it will be replaced by test.prisma:Reply.

Configuration

All the configuration in the following parts will be done in your configuration file. This configuration file can be changed by using --config when running a command (default: prisma-util.config.json).

Include Files

To save on computational power, Prisma Util doesn't look for files and instead it requires you to add each file to the configuration file.

{
    "includeFiles": ["first.prisma", "second.prisma"]
}

Paths in includeFiles are relative to your project root.

Important!

Don't add your base schema file inside of includeFiles, but in the baseSchema property. See this link for more information.

Excluding Models

This section allows you to exclude models from the generated schema. Array elements follow the naming concepts from Model & Column Representation, except the column part.

{
    "excludeModels": ["first.prisma:User", "second.prisma:Notification"]
}

Paths in excludeModels are relative to your project root.

Base Schema

Prisma Util requires a base schema file to work with. This schema file must define a generator and datasource, otherwise you'll get an error.

{
    "baseSchema": "base.prisma"
}

The path in baseSchema is relative to your project root.

Cross-file Relations

Cross-files relations are relations that use model names from other files. Cross-file relations is an experimental feature and as such you have to opt-in in order to use it.

{
    "crossFileRelations": true
}

Defining Cross-file Relations

A cross-file relation is defined as a mapping between one column and one model. All map elements follow the naming conditions described in Model & Column Representation.

What a cross-file relation essentially does is removing (if present) the model (also called a polyfill in Prisma Util, see this) from the generated file and replace all specified relations to it with the alternative mapping from the configuration file.

Example configuration section:

{
    "relations": {
        "posts.prisma:Post.user": "users.prisma:User"
    }
}

posts.prisma:

model User {
    id Int @id @default(autoincrement())
    posts Post[]
}
model Post {
    id Int @id @default(autoincrement())
    content String
    userId Int
    user User @relation(fields: [userId], references: [id])
}

users.prisma:

model User {
    id Int @id @default(autoincrement())
    username String
    email String
}

This example would generate the following schema:

model User {
    id Int @id @default(autoincrement())
    username String
    email String
    posts Post[]
}

model Post {
    id Int @id @default(autoincrement())
    content String
    userId Int
    user User @relation(fields: [userId], references: [id])
}

Paths in relations are relative to your project root.

Extending Models & Inheritance

Prisma Util allows you to extend models from any file. Extending a model will copy all of the non-id non-relation columns to the child. This sections uses the concepts from Model & Column Representation.

Example configuration section:

{
    "extended": {
        "schema.prisma:Topic": "test.prisma:Post"
    }
}

schema.prisma:

model Topic {
  id Int @id @default(autoincrement())
  body String
  owner Int
}

test.prisma:

model Post {
    id Int @id @default(autoincrement())
    title String
}

This example would generate the following schema:

model Topic {
    id Int @id @default(autoincrement())
    body String
    owner Int
    title String
}

Paths in extended are relative to your project root.

Support

Create a bug report for Prisma Util

If you see an error with Prisma Util, please create a bug report here.

Submit a feature request

If you want to see a new feature added to Prisma Util, please create an issue here.

Contributing

Refer to our contribution guidelines for information on how to contribute.

Comments
  • [v1.4.0] Additional ways of configuring your schema (YAML and XML)

    [v1.4.0] Additional ways of configuring your schema (YAML and XML)

    Additional ways of configuring schemas without the use of .env files

    As with the code-generated schemas proposal, I've seen many people interested in this feature. Basically, allow the DATABASE_URL environment variable to be used from another file, such as YAML and XML.

    Design

    My aim is to make this system as easy as possible and as such I thought about this feature as a perfect fit for the Experimental tag. To use it, you'd have to add 2 more keys in your configuration file:

    {
        "additionalFormats": true,
        "configurations": ["configuration.yml", "another.yml"]
    }
    

    As with any experimental feature, you have to add additionalFormats to enable the feature. The configurations array provides a list of relative paths for the YAML and XML files (by the way, you can suggest more formats!).

    In this example, the another.yml file would take priority and override configuration.yml, as it's the one set at the end.

    Example

    As with all proposals, here's what I think that this configuration could look like:

    datasource:
        provider: "postgresql"
        url: "DATABASE_URL"
    

    Wrapping Up

    Let me know what you think about this proposal and any improvements you'd like to see!

    enhancement 
    opened by DavidHancu 2
  • [v1.4.0] Creating schemas using JavaScript

    [v1.4.0] Creating schemas using JavaScript

    Creating schemas with JavaScript and TypeScript

    Hey everyone! I've recently got an idea from seeing how many people requested this, so here is my proposal.

    Design

    My aim is to make this system as easy as possible and as such I thought about this feature as a perfect fit for the Experimental tag. To use it, you'd have to add 2 more keys in your configuration file:

    {
        "codeSchemas": true,
        "codeGenerators": ["file.generator.js"]
    }
    

    As with any experimental feature, you have to add codeSchemas to enable the feature. The codeGenerators array provides a list of relative paths for the config generators.

    Code

    This is an example of how I thought the code part could work:

    import { SchemaCreator, Constraints } from "@prisma-util/schema-creator";
    
    export default async function createSchema()
    {
        SchemaCreator.model("User")
                                 .column("id", "Int", Constraints.ID, Constraints.DEFAULT("autoincrement"))
                                 .column("name", "String", Constraints.UNIQUE)
                                 .column("posts", "Post[]");
        SchemaCreator.model("Post")
                                 .column("id", "Int", Constraints.ID, Constraints.DEFAULT("autoincrement"))
                                 .column("title", "String", Constraints.UNIQUE)
                                 .column("userId", "Int")
                                 .column("user", "User", Constraints.RELATION(["userId"], ["id"]);             
        return SchemaCreator.build();
    }
    

    This would create the schema:

    model User {
        id Int @id @default(autoincrement())
        name String @unique
        posts Post[]
    }
    
    model Post {
        id Int @id @default(autoincrement())
        title String @unique
        userId Int
        user User @relation(fields: [userId], references: [id])
    }
    

    As you can see, everything you need is under the Constraints object. Some values (like DEFAULT and RELATION in this example) are callable, and the value you provide will be passed as an argument in your schema.

    Wrapping Up

    Let me know what you think about this proposal and any improvements you'd like to see!

    enhancement 
    opened by DavidHancu 2
  • Enhanced Introspection - Accepted Proposal

    Enhanced Introspection - Accepted Proposal

    Scope

    The aim of this proposal is to provide a sneak-peek for the new feature that will be added to Prisma Util v2.0. In this issue, I'll be outlining the process of how you can use the new Enhanced Introspection features to manipulate your schema.

    Introduction

    This feature will be released under the enhancedIntrospection flag and will require manual activation. Because this feature uses Project Toolchain's Generation API, it will create the typings necessary for configuring the fields of this object. All Enhanced Introspection steps will be ran before the schema is generated, to allow you to modify the models before they are written to the final file.

    Configuration

    To configure the Enhanced Introspection feature, you're going to have to add an introspection property to your configuration object like this:

    export default {
        // Other configuration options
        introspection: {
            modelPatterns: {
                // We'll come back to this option later on.
            }
        }
    }
    

    We've created a separate introspection object to facilitate a fieldPatterns option coming in the future.

    Model Patterns

    Model Patterns are used for defining rules that models should follow. To offer more granular control for your schema, we've implemented the following class that will be passed to your functions:

    abstract class Model {
        /**
         * The name of this model. If this parameter hasn't been modified before, it will be the table name from the database.
         */
        public abstract get name();
        /**
         * Set the name for this model.
         */
        public abstract set name(name: string);
        /**
         * Add an attribute to this model.
         * @param attribute The attribute to add. You can use the `schema-creator` module for a list of attributes.
         */
        public abstract addAttribute(attribute: string);
    }
    

    The rules for Model Patterns are split into 3 categories:

    • Static Changes

    Static changes edit models based on their name and are placed under the $static key. These can be configured in 2 ways:

    1. Using a String

    Using a String is the easiest way to remap a table. To do it, add the following object to your introspection block:

    export default {
        // Other configuration options
        introspection: {
            modelPatterns: {
                $static: {
                    "table_name": "ModelName"
                }
            }
        }
    }
    

    In this example, ModelName will map to table_name.

    1. Using a Function

    Using a function allows more granular control for this table. To do it, add the following object to your introspection block:

    import { Constraints } from "prisma-util/schema-creator";
    
    export default {
        // Other configuration options
        introspection: {
            modelPatterns: {
                $static: {
                    "table_name": async (model: Model) => {
                        model.name = "ModelName";
                        model.addAttribute(Constraints.Model.UNIQUE(["name", "email"]));
                        return model;
                    }
                }
            }
        }
    }
    

    In this example, ModelName will map to table_name and will add the @@unique([name, email]) attribute to the model.

    • Regex Changes

    Regex changes use regexes to match model names. These regexes are ran with the gims flags and are placed under the $regex key. To configure a regex replacer, add a function like this:

    export default {
        // Other configuration options
        introspection: {
            modelPatterns: {
                $regex: {
                    "model_name(\\d)": async (model: Model, match: string, ...groups: string[]) => {
                        model.name = `ModelName${groups[1]}`;
                        return model;
                    }
                }
            }
        }
    }
    

    One regex replacer can be called for multiple tables and will pass the correct model as a parameter. In this example, all tables that match the regex will have the underscore removed and will be changed to PascalCase.

    • Catch-all Changes

    Catch-all replacers will be ran for all models. To configure a catch-all replacer, add a function like this:

    export default {
        // Other configuration options
        introspection: {
            modelPatterns: {
                $all: async (model: Model) => {
                    model.name = model.name.replace(/_/gims, "");
                    return model;
                }
            }
        }
    }
    

    In this example, all tables will have the underscore removed.

    Handling Conflicts

    Because the Enhanced Introspection step is ran before the Conflict Manager has a chance to run, all models that will trigger a naming conflict will be flagged and the normal rename/skip dialog will be shown.

    Prioritizing Changes

    Replacers will be ran from the first key to the last key. A table can trigger multiple replacers, if the latter ones match the new name set by the previous replacers. You can use this to your advantage: by reordering the $static, $regex and $all keys you can create a powerful workflow that fits your use-case. One example of such workflow would be leaving the $all key as the last property of the object to enforce default naming conventions on all models.

    opened by DavidHancu 1
  • Middleware Context API - Accepted Proposal

    Middleware Context API - Accepted Proposal

    Scope

    The aim of this proposal is to provide a sneak-peek for the new feature that will be added to Prisma Util v2.0. In this issue, I'll be outlining the process of how Middleware Context can be used for passing down metadata to middleware.

    Introduction

    This feature will be released under the middlewareContext flag and will require manual activation. Because this feature uses Project Toolchain's Generation API, it will create the typings necessary for configuring context.

    Code Example

    prisma.user.findMany({
        where: {
            name: "David"
        },
        // The new Context API
        context: {
            anyKey: "anyValue",
            anotherKey: 2
        }
    });
    

    Then, in your middleware:

    const middleware = async (params, next) => {
        if(params.args.context && params.args.context.anotherKey == 2)
        {
            // do something
        }
        return next(params);
    }
    

    Middleware

    This feature makes use of Project Toolchain's Middleware API and defines a middleware called contextRemover. To use it, you have to import it like this and add it to your Prisma middleware chain:

    import contextRemover from "prisma-util/toolchain/middleware/middlewareContext";
    prisma.$use(contextRemover(prisma));
    

    And that's it! Now everything will function correctly whenever you create a new model.

    Caveat

    This context remover middleware must be the last one in the chain, otherwise you'll be removing the context from further middleware. This must be done to ensure that the context parameter isn't sent to Prisma.

    opened by DavidHancu 1
  • Custom functions for @default - Accepted Proposal

    Custom functions for @default - Accepted Proposal

    Scope

    The aim of this proposal is to provide a sneak-peek for the new feature that will be added to Prisma Util v2.0. In this issue, I'll be outlining the process of defining a function and how the configuration will look like, so you can make preparations in advance.

    Introduction

    This feature will be released under the customAttributeFunctions flag and will require manual activation.

    Configuration

    To set a default function for a field, we can just do this:

    function generateDefault(dmmf) {
        // Generate your default value based on the DMMF data of this field
        const defaultValue = "";
        return defaultValue;
    }
    export default {
        // Other configuration values
        defaultFunctions: {
            "path/to/file:ModelName.field": generateDefault
        }
    }
    

    The function will then be grabbed by Prisma Util and added to a function map. To copy the function over, Prisma Util will use .toString() on the provided function and wrap the code in another function, as such:

    const columnMappings = {
        "ModelName.field": function() {
            const func = function generateDefault(dmmf) {
                // Generate your default value based on the DMMF data of this field
                const defaultValue = "";
                return defaultValue;
            }
            return func;
        }(),
    }
    

    Middleware

    This feature makes use of Project Toolchain's Middleware API and defines a middleware called attributeFunctions. To use it, you have to import it like this and add it to your Prisma middleware chain:

    import attributeFunctions from "prisma-util/toolchain/middleware/customAttributeFunctions";
    prisma.$use(attributeFunctions(prisma));
    

    And that's it! Now everything will function correctly whenever you create a new model.

    Final Words

    Please let us know if this is suitable for you and if you would like any changes to be made.

    opened by DavidHancu 1
  • [Proposal] pg_trgm Support

    [Proposal] pg_trgm Support

    pg_trgm Support

    Hey everyone! I've recently seen an issue in the official Prisma GitHub repository asking for pg_trgm support, then encountered the same necessity with my use-cases of Prisma, so I thought about implementing it in Prisma Util.

    Design

    As all features that modify the way that Prisma Util processes files, this feature will be marked as Experimental as well (or opt-in if you will). To get started, you'd need to add the following to your configuration file (using the new .mjs syntax coming in v1.3.0):

    {
        pg_trgm: true,
        ftsIndexes: {
            "schema.prisma:User.name": "GIN"
        }
    }
    

    In this case, Prisma Util will create a GIN index for the model User in the file named schema.prisma for the name column.

    Implementation

    Prisma Util is already capable of enhanced parsing, so my idea was to add an @Unsupported("TSVECTOR") field to the schema, as well as install pg_trgm and create GIN or GiST (configured per field via the configuration file) indexes.

    Code

    This is the biggest problem right now and I'd love some feedback on how you'd want this feature to operate. I would say that a middleware is best, as it requires the least amount of setup and it's pretty easy to implement as well.

    Wrapping Up

    Please let me know what you expect from this feature and how you think that it should be used.

    enhancement 
    opened by DavidHancu 1
Releases(v2.0.0)
Owner
David Hancu
Hi, I'm David and I adore coding stuff in JavaScript. I'm 16 years old and still a student. If you want to talk, feel free to message me.
David Hancu
Tool made to easily merge multiple GTA 5 vehicle meta files.

mmVehiclesMetaMerger Tool made to easily merge multiple GTA5 vehicle meta files. Showcase Go to Youtube video. Download Click here to go to the releas

Mateusz Mleczek 11 Jan 2, 2023
A simple CLI to generate a starter schema for keystone-6 from a pre-existing prisma schema.

Prisma2Keystone A tool for converting prisma schema to keystone schema typescript This is a proof of concept. More work is needed Usage npx prisma2key

Brook Mezgebu 17 Dec 17, 2022
Merge multiple JSON files - Vanilla JavaScript and HTML (graphic mode: browser+explorer)

JSON Merger Usage First, git clone project git clone https://github.com/mtacnet/json-merger.git Move to cloned repository and open generate.html with

Tac 1 Sep 18, 2022
Prisma +2 generator to emit Yup schemas from your Prisma schema

Prisma Yup Generator Automatically generate Yup schemas from your Prisma Schema, and use them to validate your API endpoints or any other use you have

Omar Dulaimi 31 Dec 24, 2022
Prisma 2+ generator to emit Joi schemas from your Prisma schema

Prisma Joi Generator Automatically generate Joi schemas from your Prisma Schema, and use them to validate your API endpoints or any other use you have

Omar Dulaimi 26 Dec 24, 2022
Prisma +2 generator to emit a tRPC shield from your Prisma schema

Prisma tRPC Shield Generator Automatically generate a tRPC Shield from your Prisma Schema. Updates every time npx prisma generate runs. Table of Conte

Omar Dulaimi 27 Dec 24, 2022
Prisma 2+ generator to emit Zod schemas from your Prisma schema

Prisma Zod Generator Automatically generate Zod schemas from your Prisma Schema, and use them to validate your API endpoints or any other use you have

Omar Dulaimi 212 Dec 27, 2022
CodeTogether is a platform that aims to bring all the developers and coders together to appreciate collaborative coding by resolving issues faced by programmers on normal IDEs/platforms

CodeTogether is a platform that aims to bring all the developers and coders together to appreciate collaborative coding by resolving issues faced by programmers on normal IDEs/platforms. It allows developers to communicate with their fellow developers or collaborators through online voice call and realtime chat. Besides, the whiteboard makes the framing of an algorithm easier by helping programmers working collaboratively to discuss and plan their approach together

Shayan Debroy 5 Jan 20, 2022
Bys is an npm/yarn library to merge your js and ts files into one file.

Bundle your scripts (bys) Bys is an npm/yarn library to merge your js and ts files into one file. Installation Use the npm or yarn package manager to

FlamesX128 3 Dec 8, 2022
Save time by managing bills & expenses, invoicing & easy reconciliation all in one app. Generate clear dynamic statements and get your reports, the way you like them

expense-manager-app (Opensource Expense Tracking App built with React) ?? Save time by managing bills & expenses, invoicing & easy reconciliation all

Muhammad Awais 3 Oct 12, 2022
A webpack plugin to enforce case-sensitive paths when resolving module

@umijs/case-sensitive-paths-webpack-plugin A webpack plugin to enforce case-sensitive paths when resolving module, similar to the well-known case-sens

UmiJS 13 Jul 25, 2022
A Promise-compatible abstraction that defers resolving/rejecting promises to another closure.

Deferred Promise The DeferredPromise class is a Promise-compatible abstraction that defers resolving/rejecting promises to another closure. This class

Open Draft 21 Dec 15, 2022
Tool for GitHub/GitLab to keep Repositories/Projects you are interested in and their Pull/Merge Requests in desktop Tray Menu

Tool for GitHub/GitLab to keep Repositories/Projects you are interested in and their Pull/Merge Requests in desktop Tray Menu. More info in User Guide.

Oleksii Bilyk 5 Jul 31, 2022
This project is created to store the name of the book and the name of its author, build with JavaScript.

awesome-books A plain javascript project that can keep track of a list of books utilizing localStorage. See live demo Built With HTML CSS Javascript T

Shaqayq Darwazi 3 Jul 19, 2022
Create a C# .NET core EntityFramework ORM from your schema.prisma file

A note of forewarning to the would-be user... This was a failure. I'm making a note here: huge regret. It's hard to overstate my dissatisfaction. ?? S

Ian Ray 9 Dec 24, 2022
This is a library to alternate and self-host the Prisma Data Proxy (cloud.prisma.io)

Alternative Prisma Data Proxy This is a library to alternate and self-host the Prisma Data Proxy (cloud.prisma.io). In order to deploy your project to

AijiUejima 60 Dec 28, 2022
Uptime monitoring RESTful API server that allows authenticated users to monitor URLs, and get detailed uptime reports about their availability, average response time, and total uptime/downtime.

Uptime Monitoring API Uptime monitoring RESTful API server that allows authenticated users to monitor URLs, and get detailed uptime reports about thei

Mohamed Magdi 2 Jun 14, 2022
📝 [WIP] Write your reports in markdown, and get them in docx.

md-report What's md-report The repo name md-report stands for both "Write your reports in markdown, and get them in docx." and "Made report again." ("

Yiyang Sun 8 Jun 12, 2022
A progressive web app to help volunteers track their volunteering activities, register vet concerns and log incident reports.

POOPS - Pets of Older Persons 2022 Introduction This is a Next.js project bootstrapped with create-next-app. Getting Started First, install yarn: npm

Coders for Causes 17 Jan 3, 2023