Giter Site home page Giter Site logo

json-api-dotnet / jsonapidotnetcore Goto Github PK

View Code? Open in Web Editor NEW
652.0 27.0 158.0 43.02 MB

A framework for building JSON:API compliant REST APIs using ASP.NET and Entity Framework Core.

Home Page: https://www.jsonapi.net

License: MIT License

C# 99.86% PowerShell 0.11% XSLT 0.04%
json-api jsonapi-server dotnet aspnet rest-api web-api

jsonapidotnetcore's Introduction

JsonApiDotNetCore

A framework for building JSON:API compliant REST APIs using .NET Core and Entity Framework Core. Includes support for Atomic Operations.

Build Coverage NuGet Chat FIRST-TIMERS

The ultimate goal of this library is to eliminate as much boilerplate as possible by offering out-of-the-box features such as sorting, filtering and pagination. You just need to focus on defining the resources and implementing your custom business logic. This library has been designed around dependency injection, making extensibility incredibly easy.

Getting Started

These are some steps you can take to help you understand what this project is and how you can use it:

About

Official documentation

Related Projects

Examples

See the examples directory for up-to-date sample applications. There is also a Todo List App that includes a JsonApiDotNetCore API and an EmberJs client.

Installation and Usage

See our documentation for detailed usage.

Models

#nullable enable

[Resource]
public class Article : Identifiable<int>
{
    [Attr]
    public string Name { get; set; } = null!;
}

Middleware

// Program.cs

builder.Services.AddJsonApi<AppDbContext>();

// ...

app.UseRouting();
app.UseJsonApi();
app.MapControllers();

Compatibility

The following chart should help you pick the best version, based on your environment. See also our versioning policy.

JsonApiDotNetCore Status .NET Entity Framework Core
3.x Stable Core 2.x 2.x
4.x Stable Core 3.1 3.1, 5
5 5
6 5
5.0.0-5.0.2 Stable 6 6
5.0.3-5.4.0 Stable 6 6, 7
7 7
5.5+ Stable 6 6, 7
7 7
8 8
master Preview 6 6, 7
7 7
8 8
openapi Experimental 6 6, 7
7 7
8 8

Contributing

Have a question, found a bug or want to submit code changes? See our contributing guidelines.

Trying out the latest build

After each commit to the master branch, a new pre-release NuGet package is automatically published to GitHub Packages. To try it out, follow the steps below:

  1. Create a Personal Access Token (classic) with at least read:packages scope.

  2. Add our package source to your local user-specific nuget.config file by running:

    dotnet nuget add source https://nuget.pkg.github.com/json-api-dotnet/index.json --name github-json-api --username YOUR-GITHUB-USERNAME --password YOUR-PAT-CLASSIC

    In the command above:

    • Replace YOUR-GITHUB-USERNAME with the username you use to login your GitHub account.
    • Replace YOUR-PAT-CLASSIC with the token your created above.

    ⚠️ If the above command doesn't give you access in the next step, remove the package source by running:

    dotnet nuget remove source github-json-api

    and retry with the --store-password-in-clear-text switch added.

  3. Restart your IDE, open your project, and browse the list of packages from the github-json-api feed (make sure pre-release packages are included).

Development

To build the code from this repository locally, run:

dotnet build

Running tests locally requires access to a PostgreSQL database. If you have docker installed, this can be propped up via:

pwsh run-docker-postgres.ps1

And then to run the tests:

dotnet test

Alternatively, to build, run all tests, generate code coverage and NuGet packages:

pwsh Build.ps1

Sponsors

JetBrains Logo   Araxis Logo

jsonapidotnetcore's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jsonapidotnetcore's Issues

Business logic

Is there any where to put business logic? Could you add en example to the readme if so?

PATCH request doesn't save relationships

PATCH request for entities having relationships doesn't save them.

For HasOne relationships possible solution is to have code like this in DefaultRepository.UpdateAsync:

_jsonApiContext.RequestEntity.Relationships.ForEach(rel =>
{
	if (rel.IsHasOne)
	{
		var propertyInfo = entity.GetType().GetProperty($"{rel.InternalRelationshipName}Id");
		propertyInfo.SetValue(oldEntity, propertyInfo.GetValue(entity));
	}
});

However with HasMany I am not sure what is the right way. Looking to what ember-data sends, it seems that "direct" (meaning that it's not many-to-many relationship) has-many relationships are not sent with entity. Instead you should save them separately.

Support for generic request meta

Currently the only (documented) way to add metadata is to implement IHasMeta in entity class. However, it might be good to have some generic way to add it to any request, regardless of the entity type, i.e. for pagination, performance metrics or some caching information.

I am not sure what would be the right place to have it, maybe as an option for DocumentBuilder somehow?

PostAsync entity id check fails for Guid type

Since Guid type does not have null value, it is always initialized to Guid.Empty and this check in JsonApiController fails (string value of Guid.Empty is "000-000...").

Looking around I was not able to find any generic way to check for empty values. So I would suggest adding method IsIdEmpty or something similar to IIdentifiable with default implementation maybe as you have it right now, or simply checking for null value. Then users could implement that method with whatever logic their ids have.

If that seems reasonable for you, I might make a pull request with that change tomorrow.

Support proper error objects

Specification proposes layout of errors part of the output. Currently available fields in Internal\Error are: title, detail and status. Specification has more fields, and library should allow user to set them.

Another issue is that it is currently formatter expects only single Error in response, it should also support ErrorCollection. Maybe a special exception JsonApiMultipleErrors, or something like that can be added, so that user can return multiple errors conveniently.

Ideally, there might be some support of validation (I don't know which one, does .Net core/EF has one?) and automatic exceptions if some fields are invalid thrown from Controller/DefaultRepository, with correct pointers to fields.

Missing Spec Tests

Fetching Data

  • Request for an empty collection should return an empty collection

A server MUST respond to a successful request to fetch a resource collection with an array of resource objects or an empty array ([]) as the response document’s primary data.

HTTP/1.1 200 OK
Content-Type: application/vnd.api+json

{
  "links": {
    "self": "http://example.com/articles"
  },
  "data": []
}
  • Relationship requests should return null if the relationship has not been set
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json

{
  "links": {
    "self": "http://example.com/articles/1/author"
  },
  "data": null
}
  • Server MUST return 404 Not Found when processing a request to fetch a relationship link URL that does not exist.

Note: This can happen when the parent resource of the relationship does not exist. For example, when /articles/1 does not exist, request to /articles/1/relationships/tags returns 404 Not Found.

  • Inclusion of multiple relationships:

The value of the include parameter MUST be a comma-separated (U+002C COMMA, “,”) list of relationship paths. A relationship path is a dot-separated (U+002E FULL-STOP, “.”) list of relationship names.

  • 400 response if relationship does not exist

If a server is unable to identify a relationship path or does not support inclusion of resources from a path, it MUST respond with 400 Bad Request.

  • Pagination links MUST appear in the links object that corresponds to a collection

The following keys MUST be used for pagination links:

  • first: the first page of data
  • last: the last page of data
  • prev: the previous page of data
  • next: the next page of data

Keys MUST either be omitted or have a null value to indicate that a particular link is unavailable.

Creating, Updating and Deleting Resources

  • 403 Forbidden for client generated Ids

A server MUST return 403 Forbidden in response to an unsupported request to create a resource with a client-generated ID.

  • Creation: The response SHOULD include a Location header identifying the location of the newly created resource.

  • Creation: The response MUST also include a document that contains the primary resource created.

  • 409:

A server MUST return 409 Conflict when processing a POST request in which the resource object’s type is not among the type(s) that constitute the collection represented by the endpoint.

  • 404:

A server MUST return 404 Not Found when processing a request that references a related resource that does not exist.

Updating Relationships

  • Can update to-many relationships through relationship link
  • Can update to-one relationships through relationship link

A server MUST respond to PATCH, POST, and DELETE requests to a URL from a to-many relationship link as described below.

PATCH /articles/1/relationships/tags HTTP/1.1
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json

{
  "data": [
    { "type": "tags", "id": "2" },
    { "type": "tags", "id": "3" }
  ]
}

Deleting

  • 404

A server SHOULD return a 404 Not Found status code if a deletion request fails due to the resource not existing.

Dasherize relationships

currently TodoItems serializes to todoItems, but should serialize to todo-items for ember data

  • dasherize response (v0.1.3)
  • de-dasherize incoming uri...
GET http://localhost:5000/api/v1/people/1/todo-items

throws

Exception has been thrown by the target of an invocation. ---> 
System.ArgumentException: 'Todo-items' is not a valid property of 'Api.Model.Person'

Move majority of reflection operations into setup

Should probably create a map of objects types, names, property names and their immediate relationships:

ObjectMap:
[{
 Name: TodoItems,
 Type: TodoItem,
 Properties: [
    "name"
 ],
 Relationships: [
   {
      Name: Owner,
      Type: Person,
      GenericType: null,
      RelationshipType: BelongsTo
   }
 ]
},
{
 Name: People,
 Type: Person,
 Properties: [
    "name"
 ],   
 Relationships: [
   {
      Name: TodoItems,
      Type: List<TodoItem>,
      GenericType: TodoItem,
      RelationshipType: HasMany
   }
 ]
}]

Missing License

This is a very interesting project! However, could you please add a license (e.g. MIT)? Otherwise it is unsure how the sources can be used and / or redistributed. With a proper license I might use this in one of my projects and of course will contribute all changes back here.

Get error when I try to filter by Guid

For example when I try to filter by a guid like below
?filter[bookingId]=bff69e53-0275-4ba2-fde0-08d45592e5f1
I get the an error
Invalid cast from 'System.String' to 'System.Guid'

Unable to override controller methods with Authorize DataAnnotation

We have JWT authentication middleware configured on our application - so any controllers with the [Authorize] data annotation will return 401 Unauthorized unless a valid token is provided in the request header.

We tried to apply the data annotation to an endpoint as follows:

    [Authorize]
    public class ReadingController : JsonApiController, IJsonApiController
    {
        private dbContext _context;

        public ReadingController(JsonApiContext jsonApiContext, ResourceRepository resourceRepository,
            dbContext dbContext) : base(jsonApiContext, resourceRepository)
        {
            _context = dbContext;
        }

        [Authorize]
        public override ObjectResult Get()
        {
            return base.Get();
        }

        [Authorize]
        public override ObjectResult Get(string id)
        {
            return base.Get(id);
        }
    }

But the endpoint returns a 200 OK response with the data even when no token is provided

Relationship links missing ids

Relationship links are fine for /todo-items/:id:

"owner": {
        "links": {
          "self": "http://localhost:5000/api/v1/todo-items/422/relationships/owner",
          "related": "http://localhost:5000/api/v1/todo-items/422/owner"
        }
      }

However, they are broken for /todo-items

"owner": {
          "links": {
            "self": "http://localhost:5000/api/v1/todo-items/relationships/owner",
            "related": "http://localhost:5000/api/v1/todo-items/owner"
          }
        }

and people/:id

"relationships": {
      "todo-items": {
        "links": {
          "self": "http://localhost:5000/api/v1/relationships/todo-items",
          "related": "http://localhost:5000/api/v1/todo-items"
        }
      }
    }

v0.2.0 Checklist [proposal]

  • Complete Sorting
  • #5 406 Response
  • #6 Error Objects
  • Pagination Links
  • Update relationship links
  • Include query params
  • Sparse Fieldsets
  • 403 Forbidden for client generated Ids:

A server MUST return 403 Forbidden in response to an unsupported request to create a resource with a client-generated ID.

Querying records

Need to be able to query records without fetching the entire dataset.

Maybe a query builder object that wraps LINQ calls...

  • GenericDataAccessAbstraction.SingleOrDefault(string name, string value)
  • GenericDataAccessAbstraction.Filter(string name, string value)

Cleanup Test Project

Some housekeeping items:

  • Split tests into 2 projects, Unit and Acceptance
  • Scrap the test documentation fixture (also update readme) <-- done in feat/core-2 branch
  • Consolidate server setup into a TestFixture
  • Update CI to run all test projects
  • Run all tests on AppVeyor (currently not running any)
  • Evaluate solution coverage and add unit tests

see constructor for NoEntityFrameworkTests created in #89 for an example of the kinds of things that should be extracted into a fixture

Compund Includes Are Missing Relationships

Andrey @jfhs Mar 16 09:29
another thing I noticed is that included records don't have their relationships
not even links to them
if that's not intended, I may open an issue about that too

Jared Nance @jaredcnance Mar 16 09:35
hmm okay i'll take a look at that this afternoon
yeah I see that now -- the relationships are never set in the DocumentBuilder I think this will be an easy fix but I'll dig a little further later on

Andrey @jfhs Mar 16 09:38
yes, I did a quick fix in my local version by just copy-pasting code from _getData

Jared Nance @jaredcnance Mar 16 09:40
that should work -- _getIncludedEntity should be able to just call _getData after it has the contextEntity

Inconsistency for dasherized attributes

If I have a client entity definition as follows:

public class Client : Identifiable
{
        [Attr("clientName")]
        public string ClientName { get; set; }
}

I expect that the attribute is everywhere referenced as "clientName". However, when POSTing a new client, I am forced to use the dasherized version of it:

{
  "data": {
    "type": "clients",
    "attributes": {
      "client-name": "Foo Bar"
  }
}

However, the response of that and a normal GET request uses the non-dasherized version:

{
  "data": {
    "type": "clients",
    "id": "8",
    "attributes": {
      "clientName": "Foo Bar"
    },
  }
}

I am forced to do that due to this line in the JsonApiDeSerialization.cs

I expect that the attribute names defined via the Attr attribute are used throughout the framework.

Make filtering extensible

Proposed by @jfhs:

I think, filter query params could be simple parsed to triplets like <key,value,operation> (as you have it now, but key and operation would be an arbitrary string). Then to support attribute filtering out of the box, in DefaultRepository.Filter check if filter key is a valid attribute, and operation is supported, then construct new FilterQuery and pass it to IQueryable.Filter
this way you can keep filtering code for EF
and then if user wants to have custom filters - he just have to override Repository.Filter and handle his custom filters before default.
and pass everything else to base class

Proposal

  • Rename FilterQuery to AttrFilterQuery
  • Create new FilterQuery with raw request (string) information
  • AttrFilterQuery accepts FilterQuery as a constructor parameter and the attribute mapping in QuerySet should be moved here
  • DefaultEntityRepository constructs AttrFilterQuery from FilterQuery
  • Document override procedure

Breaking Changes

  • The FilterQuery class is changing and could affect custom implementations of IEntityRepository

PATCH request with nullable attributes does not work

Hi,
Thanks for this great library!

When you are trying to PATCH a model with nullable attributes, it does not work.

Model example:

public class TodoItem : Identifiable
{
    Attr("description")]
    public string Description { get; set; }

    [Attr("created-date")]
    public DateTime CreatedDate { get; set; }

    [Attr("achieved-date")]
    public DateTime? AchievedDate { get; set; }
}

1st PATCH Request example: Field is not sent
PATCH http://localhost:5000/api/todoitem/1

{
  "description": "New description"
}

2nd PATCH Request example: Field is sent
PATCH http://localhost:5000/api/todoitem/1

{
  "description": "New description",
  "achieved-date": "2017-04-25T21:41:29.033Z"
}

Actual behavior: Exception is returned from the API.
Expected behavior:

  • Field is ignored if not sent (1st example)
  • Field is updated with new value if sent (2nd example)

Gerfaut

Common method for setting the context

All data access methods that are called directly in DefaultEntityRepository should use Get() instead of _dbSet directly. This will allow custom implementations to set the context for all queries by overriding the Get() method only.

Example
In DefaultEntityRepository.cs#L77

return await _dbSet.SingleOrDefaultAsync(e => e.Id.Equals(id));

can be changed to

return await Get().SingleOrDefaultAsync(e => e.Id.Equals(id));

v0.1.0 Checklist

  • Middleware should check whether or not the route has been configured, create the controller instance, call the specified method:
    • GET /{namespace}/{entities}/
    • GET /{namespace}/{entities}/{id}
    • POST /{namespace}/{entities}/
    • PATCH /{namespace}/{entities}/{id}
    • DELETE /{namespace}/{entities}/{id}
  • Support Relationship Links:
  • Check to see if there is a controller override specified (interface vs. abstract vs. concrete controller), if so call the override methods instead
  • Convert entities to JsonApiDocuments
  • Convert JsonApiDocuments to entities
  • End the request pipeline
  • Consider using AutoMapper to map from the model to a JsonApiResource
  • CI build setup
  • #3 : Resource mapping
  • #9 : Querying Records

Optional relationships

Hi. I have optional relationships in my models.

class Model : Identifiable<int>
{
  public override int Id {get;set;}
  public int? FooId {get;set;}
  public int? BarId {get;set;}
  public int? BazId {get;set;}

  public virtual A Foo {get;set;}
  public virtual B Bar {get;set;}
  public virtual C Baz {get;set;}
}

For every model only one of this relationships can exists. When i do query like this

http://localhost:5000/models?include=foo,bar,baz

i catch exception:

System.NullReferenceException: Object reference not set to an instance of an object.
   at System.Object.GetType()
   at JsonApiDotNetCore.Builders.DocumentBuilder._getRelationship(Object entity, String relationshipName)
   at JsonApiDotNetCore.Builders.DocumentBuilder.<>c__DisplayClass8_0.<_addRelationships>b__0(Relationship r)
   at System.Collections.Generic.List`1.ForEach(Action`1 action)
   at JsonApiDotNetCore.Builders.DocumentBuilder._getData(ContextEntity contextEntity, IIdentifiable entity)
   at JsonApiDotNetCore.Builders.DocumentBuilder.Build(IEnumerable`1 entities)
   at JsonApiDotNetCore.Serialization.JsonApiSerializer._serializeDocuments(Object entity, IJsonApiContext jsonApiContext)
   at JsonApiDotNetCore.Formatters.JsonApiOutputFormatter.<WriteAsync>d__1.MoveNext()

As i understand, problem is here - https://github.com/Research-Institute/json-api-dotnet-core/blob/master/src/JsonApiDotNetCore/Builders/DocumentBuilder.cs#L209 cause entity is null.

Should there be check for null? I can make PR if needed.

Use Attributes for determining relationships

Currently, assumes all virtual members are relationships which is incorrect.
Should use an attribute to determine relationships which will also simplify evaluation later.

[HasMany("todo-items")]
public virtual List<TodoItem> TodoItems { get; set; }

[HasOne("owner")]
public virtual Person Owner { get; set; }

406 Not Acceptable

Servers MUST respond with a 406 Not Acceptable status code if a request’s Accept header contains the JSON API media type and all instances of that media type are modified with media type parameters.

Add 'LIKE' filter

There should be a way to execute LIKE statements in queries. Possibly in the form:

filter[attribute]=like:value

Wrong links for relationships when plural name is different from singular

Get request to /api/v1/cities where City is following class:

public class City : Identifiable<Guid>
    {
        [Attr("name")]
        public string Name { get; set; }

        public Guid CountryId { get; set; }

        [HasOne("country")]
        public virtual Country Country { get; set; }
    }

Returns wrong links for country relationship for one entity:

{
  "links": {
    "related": "http://localhost:2628/api/v1/cities/city/ceae06ec-906a-4ca9-a458-41a86f5cdb56/country",
    "self": "http://localhost:2628/api/v1/cities/city/ceae06ec-906a-4ca9-a458-41a86f5cdb56/relationships/country"
  }
}

When actually links should be like:

{
  "links": {
    "related": "http://localhost:2628/api/v1/cities/ceae06ec-906a-4ca9-a458-41a86f5cdb56/country",
    "self": "http://localhost:2628/api/v1/cities/ceae06ec-906a-4ca9-a458-41a86f5cdb56/relationships/country"
  }
}

However, correct links are given if requesting single item, i.e. "/api/v1/cities/123"

As far as I understand, the problem is in LinkBuilder.GetNamespaceFromPath, it checks only for singular name. However I am not sure if changing this wouldn't break something else.

Deeply Nested Relationship Inclusion

GET /articles/1?include=comments.author HTTP/1.1
Accept: application/vnd.api+json

Note: Because compound documents require full linkage (except when relationship linkage is excluded by sparse fieldsets), intermediate resources in a multi-part path must be returned along with the leaf nodes. For example, a response to a request for comments.author should include comments as well as the author of each of those comments.

Note: A server may choose to expose a deeply nested relationship such as comments.author as a direct relationship with an alias such as comment-authors. This would allow a client to request /articles/1?include=comment-authors instead of /articles/1?include=comments.author. By abstracting the nested relationship with an alias, the server can still provide full linkage in compound documents without including potentially unwanted intermediate resources.

Will also need to include support for Sparse Fieldsets of included relationships

Filter by attribute of related entity

Maybe this is possible already but it would be great to be able to filter by an attribute of a related entity
Maybe, have something like
bookings?include=status&filter[starting-at]=le:2017-01-01&filter[status.name]=eq:active

Add possibility to add custom non-JSONAPI controllers

Currently, via the DasherizedRoutingConvention, all controllers are considered as JSONAPI controllers and the routing convention is applied making it impossible to use that convention besides other non-JSONAPI controllers with custom attribute routing.

It should be possible to explicitly define which controllers are considered as JSONAPI controllers. Easiest way would be a simple attribute that's attached to a controller and the DasherizedRoutingConvention applies the convention only to those controllers with the attribute.

HasMany relationship treated as HasOne in deserialization

Having a model

    public class Country: Identifiable<Guid>
    {
        [Attr("name")]
        public string Name { get; set; }

        [HasMany("cities")]
        public virtual List<City> Cities { get; set; }
    }

And trying to post

{"data": {
    "attributes": {
      "name":"qwe"
    },
    "relationships": {
      "cities": {
        "data": []
      }
    },
    "type":"countries"
  }
}

JsonApiDeSerializer tries to find CitiesId property in here which it of course doesn't.

I believe there should be separate code for handling HasMany relationships.

RFC: De-Couple data access from EF

Problem

Currently, the controller leans on IEntityRepository directly which is tightly coupled to IQueryable. However, this doesn't make much sense for ORMs, like Dapper, that do not use IQueryable. Also, it is entirely possible that resources may be retrieved from non SQL storage solutions, such as another HTTP API or RPC.

Tightly Coupled Areas:

Proposed Solution

  1. Extract controller logic into IResourceService. Reduces controller methods to something like the following. Would allow any intermediate service to perform the data access.
public class JsonApiController<T, TId>
    : JsonApiControllerMixin where T : class, IIdentifiable<TId>
{
    private readonly IResourceService<T, TId> _service;

    public JsonApiController(IResourceService<T, TId> resourceService)
    {
        _service = resourceService;
    }

    [HttpGet]
    public virtual async Task<IActionResult> GetAsync()
    {
        var entities = await _service.GetAsync();
        return Ok(entities);
    }
}
  1. Refactor the context graph:
  • Rename ContextGraphBuilder to EntityContextGraphBuilder, since it uses the EF DbContext.
  • Can ContextGraph be used without the DbContext generic? If not, perform rename Entity...
  • Define API to either inject custom IContextGraph implementations or an API to build a generalized context graph
  1. Refactor service extensions or add methods for using the new APIs defined by (2)

Considerations

  • Need to take a closer look at json:api operations and how this would fit in.
  • Since this may introduce a variety of breaking changes, perhaps we should consider further de-coupling from the MVC middleware in this solution as well (see discussion in #71)?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.