Skip to content

Getting started

Welcome to the CommonGrants protocol documentation. This guide will help you get started with understanding and implementing the protocol.

What is CommonGrants?

CommonGrants is a protocol designed to standardize how data about funding opportunities, applications, and awards is shared across the grant ecosystem.

The CommonGrants protocol is built on top of TypeSpec, a language for describing APIs that compiles to OpenAPI, JSON Schema, and other formats. To simplify the process of adopting this protocol, CommonGrants has published both:

Quickstart

The following tutorial will walk you through:

  • Creating a new CommonGrants API specification using TypeSpec
  • Compiling your project to OpenAPI and previewing it with Swagger
  • Extending the base CommonGrants spec to include a custom field
  • Viewing this new field in the OpenAPI docs

First steps

First, we’ll show you how to set up and preview the default CommonGrants API specification using the CLI and quickstart template.

  1. Install the CLI and TypeSpec compiler globally

    Terminal window
    npm install @common-grants/cli @typespec/compiler -g
  2. Create a new directory for your project

    Terminal window
    mkdir common-grants-quickstart
    cd common-grants-quickstart
  3. Initialize your project with the quickstart template.

    Terminal window
    cg init --template quickstart

    Follow the prompts and accept the default options.

    Terminal window
    TypeSpec compiler v0.63.0
    Example from the quickstart guide
    Project name common-grants-quickstart
    Do you want to generate a .gitignore file? yes
    Update the libraries? @common-grants/core
    TypeSpec init completed. You can run `tsp install` now to install dependencies.
    Project created successfully.
  4. Install the project dependencies

    Terminal window
    tsp install
  5. Compile and preview your API specification at http://localhost:3000

    Terminal window
    cg compile main.tsp
    cg preview tsp-output/@typespec/openapi3/openapi.Quickstart.yaml

Deep dive

Next, we’ll take a step back and walk you through what happened behind the scenes when you ran the CLI commands listed above.

Explore the files created by cg init

When you run cg init it sets up a new CommonGrants project in the common-grants-quickstart directory with the following files:

  • main.tsp # Defines the API service
  • routes.tsp # Defines the API routes
  • tspconfig.yaml # Configures TypeSpec emitters (e.g. OpenAPI)
  • package.json # Manages project dependencies
  • .gitignore # Ignores files from the project
  • README.md # The project README

We’ll focus on exploring the following files in particular:

Understanding routes.tsp

The routes.tsp file is the TypeSpec file that defines the API routes.

routes.tsp
import "@common-grants/core";
using TypeSpec.Http;
@tag("Opportunities")
@route("/common-grants/opportunities")
namespace Quickstart.Routes {
alias OpportunitiesRouter = CommonGrants.Routes.Opportunities;
op list is OpportunitiesRouter.list;
op read is OpportunitiesRouter.read;
op search is OpportunitiesRouter.search;
}

Since there’s a lot happening here, let’s break it down section by section.

  1. Import the CommonGrants TypeSpec library.

    import "@common-grants/core";
  2. Expose the TypeSpec.Http namespace.

    using TypeSpec.Http;

    This makes the @route and @tag keywords available in the current namespace without requiring the TypeSpec.Http prefix.

  3. Define a namespace for the OpportunitiesRouter.

    @tag("Opportunities")
    @route("/common-grants/opportunities")
    namespace Quickstart.Routes {
    alias OpportunitiesRouter = CommonGrants.Routes.Opportunities;
    // operations omitted for brevity
    }

    Think of the OpportunitiesRouter as a controller in a traditional MVC framework that groups related routes together under the Opportunities tag and exposes them at the /common-grants/opportunities path.

  4. Expose the default routes from the CommonGrants library.

    @tag("Opportunities")
    @route("/common-grants/opportunities")
    namespace Quickstart.Routes {
    alias OpportunitiesRouter = Opportunities;
    op list is OpportunitiesRouter.list;
    op read is OpportunitiesRouter.read;
    op search is OpportunitiesRouter.search;
    }

    This exposes the following default routes from the CommonGrants library:

    • GET /common-grants/opportunities
    • GET /common-grants/opportunities/{id}
    • POST /common-grants/opportunities/search
Understanding main.tsp

The main.tsp file is the TypeSpec file that defines the API service.

main.tsp
import "@typespec/http";
import "./routes.tsp";
using TypeSpec.Http;
/** API description here */
@service({
title: "Quickstart API",
})
namespace Quickstart;

Let’s break down the main.tsp file section by section.

  1. Import the TypeSpec HTTP module and the routes.tsp file.

    import "@typespec/http";
    import "./routes.tsp";

    Importing the routes.tsp includes the routes from that file in the OpenAPI document generated by the cg compile command.

  2. Expose the TypeSpec.Http namespace.

    using TypeSpec.Http;

    This makes the @service keyword available in the current namespace without requiring the TypeSpec.Http prefix.

  3. Define the API service.

    /** API description here */
    @service({
    title: "Quickstart API",
    })
    namespace Quickstart;

    This defines the API service with a title of “Quickstart API”. The @service keyword is used to define the API service and is required for the OpenAPI emitter to work. The title property is used to set the title of the API and the docstring is used to set the description of the API in the OpenAPI docs.

Understanding tspconfig.yaml

The tspconfig.yaml file is the TypeSpec configuration file that defines the emitters for the project.

tspconfig.yaml
emitters:
- "@typespec/openapi3"
- "@typespec/json-schema"

This configures the TypeSpec compiler to emit the API definition as an OpenAPI document and a JSON Schema.

Explore the packages installed by tsp install

When you run tsp install, the following packages are installed (among others):

  • @common-grants/core The CommonGrants TypeSpec library, which includes a default set of models and routes that can be used to define your own CommonGrants API.
  • @typespec/openapi3 The OpenAPI emitter for TypeSpec, which emits the CommonGrants API definition as an OpenAPI document.
  • @typespec/json-schema The JSON Schema emitter for TypeSpec, which emits the models in your CommonGrants API as JSON Schemas.

Explore the files created by cg compile

This will create a tsp-output directory with the following files compiled from main.tsp:

  • Directorytsp-output/
    • Directory@typespec/
      • Directoryopenapi3/ Auto-generated by the OpenAPI emitter
        • openapi.CommonGrants.yaml The base CommonGrants API spec
        • openapi.Quickstart.yaml The quickstart API spec
      • Directoryjson-schema/ Auto-generated by the JSON Schema emitter
        • CustomEnumValue.yaml
        • Opportunity.yaml

Understanding the cg preview command

The cg preview command starts a local server that serves the OpenAPI specification using Swagger UI. View your API specification at http://localhost:3000.

Customization

Finally, we’ll show you how to extend the base CommonGrants API specification with a custom field.

Add a custom field to the Opportunity model

Create a new file called models.tsp with the following code:

models.tsp
import "@common-grants/core";
// Allows us to use models defined in the specification library
// without prefixing each model with `CommonGrants`
using CommonGrants.Models;
using CommonGrants.Fields;
namespace Quickstart.Models;
model Agency extends CustomField {
name: "Agency";
type: CustomFieldType.string;
value: string;
description: "The agency managing the funding opportunity.";
}
// Create a custom Opportunity type using the template
model CustomOpportunity extends OpportunityBase {
@example(#{
agency: #{
name: "Agency",
type: CustomFieldType.string,
value: "Department of Energy",
description: "The agency managing the funding opportunity.",
},
})
customFields: {
agency: Agency;
};
}

There’s a lot happening here, so let’s break it down section by section.

  1. Import the CommonGrants TypeSpec library.

    import "@common-grants/core";
  2. Make the CommonGrants fields, models, etc. available in the current namespace.

    using CommonGrants.Fields;
    using CommonGrants.Models;

    Without this, you would have to prefix each model with CommonGrants.Models and each field with CommonGrants.Fields. For example CommonGrants.Fields.CustomField.

  3. Declare a namespace for the current file.

    namespace Quickstart.Models;
  4. Define a custom field by extending the CustomField model.

    model Agency extends CustomField {
    name: "Agency";
    type: CustomFieldType.string;
    value: string;
    description: "The agency managing the funding opportunity.";
    }

    The Agency model defines literal values for the field’s name, type, and description metadata properties, and requires the value property to be a string.

  5. Extend the OpportunityBase model to include this custom field.

    model CustomOpportunity extends Models.OpportunityBase {
    // Example omitted for brevity
    customFields: {
    agency: Agency;
    };
    }

    It also provides an example of the updated customFields property, which will be displayed in the OpenAPI docs.

    model CustomOpportunity extends Opportunity.OpportunityBase {
    @example(
    #{
    agency: #{
    name: "Agency",
    type: Fields.CustomFieldType.string,
    value: "Department of Energy",
    description: "The agency managing the funding opportunity."
    }
    }
    )
    customFields: {
    agency: Agency;
    };
    }

Update the default routes

Update the routes.tsp file and make the following changes:

  • Import the models.tsp file to expose the CustomOpportunity model.
  • Update the routes to use the CustomOpportunity model.
routes.tsp
import "@common-grants/core";
import "./models.tsp";
using TypeSpec.Http;
@tag("Opportunities")
@route("/common-grants/opportunities")
namespace Quickstart.Routes {
alias OpportunitiesRouter = CommonGrants.Routes.Opportunities;
op list is OpportunitiesRouter.list;
op read is OpportunitiesRouter.read;
op search is OpportunitiesRouter.search;
op list is OpportunitiesRouter.list<Models.CustomOpportunity>;
op read is OpportunitiesRouter.read<Models.CustomOpportunity>;
op search is OpportunitiesRouter.search<Models.CustomOpportunity>;
}

Let’s explain these changes section by section.

  1. Import the models.tsp file to expose the CustomOpportunity model.

    import "./models.tsp";
  2. Update the default routes to use the CustomOpportunity model.

    @tag("Opportunities")
    @route("/common-grants/opportunities")
    namespace Quickstart.Routes {
    alias OpportunitiesRouter = Opportunities;
    op list is OpportunitiesRouter.list<Models.CustomOpportunity>;
    op read is OpportunitiesRouter.read<Models.CustomOpportunity>;
    op search is OpportunitiesRouter.search<Models.CustomOpportunity>;
    }

    This updates GET /opportunities and GET /opportunities/{id} to return our CustomOpportunity model instead of the default model from the CommonGrants library.

The updated routes.tsp file should now look like this:

routes.tsp
import "@common-grants/core";
import "./models.tsp";
using TypeSpec.Http;
@tag("Opportunities")
@route("/common-grants/opportunities")
namespace Quickstart.Routes {
alias OpportunitiesRouter = CommonGrants.Routes.Opportunities;
op list is OpportunitiesRouter.list<Models.CustomOpportunity>;
op read is OpportunitiesRouter.read<Models.CustomOpportunity>;
op search is OpportunitiesRouter.search<Models.CustomOpportunity>;
}

View your changes

Terminal window
cg compile main.tsp
cg preview tsp-output/@typespec/openapi3/openapi.Quickstart.yaml

This should start a local server that serves the OpenAPI specification using Swagger UI. View your API specification at http://localhost:3000.

Next steps

Learn more about the CommonGrants protocol:

Learn more about TypeSpec: