All Projects → oslofjord → sanity-linq

oslofjord / sanity-linq

Licence: MIT license
Strongly-typed .Net Client for Sanity

Programming Languages

C#
18002 projects
javascript
184084 projects - #8 most used programming language

Projects that are alternatives of or similar to sanity-linq

Nhibernate Core
NHibernate Object Relational Mapper
Stars: ✭ 1,918 (+5227.78%)
Mutual labels:  linq, linq-provider
PropertyTranslator
Translates computed properties in LINQ queries into their implementation (based on Microsoft.Linq.Translations).
Stars: ✭ 14 (-61.11%)
Mutual labels:  linq, linq-provider
startup-starter-kit
The Structured Content Startup Starter Kit
Stars: ✭ 42 (+16.67%)
Mutual labels:  sanity, headless-cms
lambda2js
Converts a C# expression tree (from Linq namespace) to a syntatically correct javascript code.
Stars: ✭ 51 (+41.67%)
Mutual labels:  linq
directus-metalsmith-snipcart
Lookbook web app with Directus' open source headless CMS, Metalsmith, Vue.js & Snipcart
Stars: ✭ 14 (-61.11%)
Mutual labels:  headless-cms
linq
A familiar set of functions that operate on JavaScript iterables (ES2015+) in a similar way to .NET's LINQ does with enumerables.
Stars: ✭ 39 (+8.33%)
Mutual labels:  linq
sanity-codegen
Generate TypeScript types from your Sanity.io schemas
Stars: ✭ 181 (+402.78%)
Mutual labels:  sanity
sanity-portfolio-studio
A Sanity.io instance that powers a Gatsby portfolio site. Watch it get built live:
Stars: ✭ 14 (-61.11%)
Mutual labels:  sanity
dotnet-arangodb
.NET Driver for ArangoDB
Stars: ✭ 52 (+44.44%)
Mutual labels:  linq
laravel-storyblok
Make Laravel and Storyblok work together beautifully.
Stars: ✭ 45 (+25%)
Mutual labels:  headless-cms
go-streams
Stream Collections for Go. Inspired in Java 8 Streams and .NET Linq
Stars: ✭ 127 (+252.78%)
Mutual labels:  linq
Beetle.js
🪲 Javascript ORM, manage your data easily.
Stars: ✭ 53 (+47.22%)
Mutual labels:  linq
nuxt-ghost
Easy Ghost content API integration with Nuxt.js.
Stars: ✭ 27 (-25%)
Mutual labels:  headless-cms
nuxt-cockpit
A Tutorial to Bundle Cockpit CMS & Nuxt.js in a full JAMstack
Stars: ✭ 45 (+25%)
Mutual labels:  headless-cms
LinqToXsdCore
LinqToXsd ported to .NET Core (targets .NET Standard 2 for generated code and .NET Core 3.1, .NET 5+ 6 for the code generator CLI tool).
Stars: ✭ 23 (-36.11%)
Mutual labels:  linq
pageflo
A new super flexible open source CMS
Stars: ✭ 34 (-5.56%)
Mutual labels:  headless-cms
linqjs
Perform queries on collections in the manner of C#s System.Linq in JavaScript
Stars: ✭ 14 (-61.11%)
Mutual labels:  linq
pocket-cms
☁️ A pocket sized CMS written for nodejs
Stars: ✭ 13 (-63.89%)
Mutual labels:  headless-cms
yllet
Yllet is a set of packages for the WordPress API for both React and non-React projects
Stars: ✭ 46 (+27.78%)
Mutual labels:  headless-cms
buzzyblog
React + WordPress REST API, a new endeavor to provide a better experience to content creators, web masters and digital marketers, etc
Stars: ✭ 50 (+38.89%)
Mutual labels:  headless-cms

Sanity LINQ

A strongly-typed .Net Client for Sanity CMS with support for LINQ queries, mutations, transactions, joins, projections and more...

Build status

Introduction

Sanity CMS is a headless CMS available at https://sanity.io with a powerful query API, file store and CDN.

Sanity LINQ was intially developed at Oslofjord Convention Center to facilitate development of .Net projects based on Sanity CMS.

Inspiration was drawn from the .Net client provided by onyboy at https://github.com/onybo/sanity-client.

The Sanity LINQ client goes beyond providing a simple HTTP client and introduces strongly typed queries, projections, mutations and joins - much in the same way as Entity Framework provides this for SQL.

Installation

Sanity.Linq is available as a NuGet package.

Install using Package Manager:

PM> Install-Package Sanity.Linq

Install using .Net CLI:

> dotnet add package Sanity.Linq

Getting Started

To get started, simply instaniate a new SanityDataContext:

var options = new SanityOptions
        {
            ProjectId = "#your-project-id#",
            Dataset = "#your-dataset#",
            Token = "#your-token#",
            UseCdn = false,
            ApiVersion = "v1"
        };

var sanity = new SanityDataContext(options);

1. Basic Queries

Start by defining entity classes which match documents in Sanity.

Alternative 1:

POCO Entity with zero dependencies on Sanity Linq.

// Example
public class Category
{
    // Use of JsonProperty to serialize to Sanity _id field.
    [JsonProperty("_id")]
    public string CategoryId { get; set; }

    // Type field is also required
    [JsonProperty("_type")]
    public string DocumentType => "category";

    public string Title { get; set; }

    public string Description { get; set; }
}

Alternative 2:

Create a class which inherits SanityDocument.

// Example
public class Author : SanityDocument
{
    public Author() : base() { }

    public string Name { get; set; }

    // etc...
}

//Example
public class Post : SanityDocument
{
    public string Title { get; set; }

    public DateTimeOffset? PublishedAt { get; set; }

    // etc...
}

Next, simply run Linq queries against the SanityDataContext:

var posts = sanity.DocumentSet<Post>();

var totalNumberOfPosts = await posts.CountAsync();
var publishedToday = await posts.Where(p => p.PublishedAt > DateTime.Today).ToListAsync();

The LINQ queries above are respectively translated to a Sanity GROQ query by Sanity Linq:

// Total number of posts:
count(*[_type == "post"])

// Published today:
*[(_type == "post") && ((publishedAt >= "2018-10-06T00:00:00.0000000+02:00"))]

2. Projections

LINQ selections are also supported by Sanity Linq:

// Returns a list of strings ordered by publish date
var postTitles = sanity.DocumentSet<Post>()
                       .OrderByDescending(p => p.PublishedAt)
                       .Select(p => p.Title)
                       .ToList();

3. Mutations

Mutations such as insert, update and delete can be performed on single documents or on multiple objects using a query!

Insert document:

var author = new Author
{
    Id = Guid.NewGuid().ToString(),
    Name = "Joe Bloggs",
};

await sanity.DocumentSet<Author>().Create(author).CommitAsync();

Update document:

var authors = sanity.DocumentSet<Author>();

var author = await authors.GetAsync("some-guid");
author.Name = "William Bloggs";

await authors.Update(author).CommitAsync();

Delete document:

var authors = sanity.DocumentSet<Author>();

// Delete by id
await authors.DeleteById("some-guid").CommitAsync();

Delete multiple documents by query:

var posts = sanity.DocumentSet<Post>();

// Delete by query
await posts.Where(p => p.Title.Contains("boring")).Delete().CommitAsync();

// Same as above:
await posts.DeleteByQuery(p => p.Title.Contains("boring")).CommitAsync();

Patch document by id:

var posts = sanity.DocumentSet<Post>();

// Patch title by id
posts.PatchById("some-guid", p => p.Set = new { Title = "New Title" }).CommitAsync();

Patch document by query:

var posts = sanity.DocumentSet<Post>();

// Patch title by query
posts.Where(p => p.Title == "").Patch(p => p.Set = new { Title = "Untitled" }).CommitAsync();

// Same as above:
posts.PatchByQuery(p => p.Title == "", p => p.Set = new { Title = "Untitled" }).CommitAsync();

4. Transactions - Bulk Mutations

The SanityDataContext keeps track of pending mutations, independent of type. To apply multiple mutations in a single transaction, simply wait with calling CommitAsync() until all mutations have been applied.

The fluent API also allows mutations to be chained.

var posts = sanity.DocumentSet<Post>();
var authors = sanity.DocumentSet<Author>();

// Delete and patch posts
posts.DeleteByQuery(p => p.Title == "")
     .PatchByQuery(p => p, p.LastUpdated = DateTime.Now)

// Add authors
authors.Create(new Author() { ... });

// Commit all changes 
await sanity.CommitAsync();

5. Joins

The Sanity Linq library includes a helper class SanityReference which models the structure of relations in Sanity:

// Example: Post related to Author and a list of Categories
public class Post : SanityDocument
{
    public string Title { get; set; }

    public SanityReference<Author> Author { get; set; }

    public SanityImage MainImage { get; set; }

    public List<SanityReference<Category>> Categories { get; set; }
}

Related documents can be included in query results in several ways.

  1. Relations are automatically followed by referencing .Value in projections:

    sanity.DocumentSet<Post>().Select(p => new { p.Title, p.Author.Value.Name });
  2. The [Include] attribute can be placed above the property if it should always be included:

    [Include]
    public SanityReference<Author> Author { get; set; }
    
    [Include]
    public SanityImage MainImage { get; set; }
    
    [Include]
    public List<SanityReference<Category>> Categories { get; set; }
    

Note that [Include] not only works on SanityReference, but also lists of references and SanityImage.

  1. References can also be selectively joined, using the Include() extension method when querying:
    await sanity.DocumentSet<Post>().Include(p => p.Author).ToListAsync();
    

6. Files and Images

The SanityDataContext has two predefined document sets for Files and Images. These document sets can be used to manages Sanity assets:

Retrieving Files and Images

// Get all files and images:
var files = await sanity.Files.ToListAsync(); 
var images = await sanity.Images.ToListAsync();    

Uploading and Linking Assets

The Filesand Images document sets also support uploading new assets, both using a Streamor by simply providing a source URL.

// Example: upload image and link to new document

// Upload new image
var imageUri = new Uri("https://www.sanity.io/static/images/opengraph/social.png");
var image = (await sanity.Images.UploadAsync(imageUri)).Document;

// Link image to new author
var author = new Author()
{
   Name = "Joe Bloggs",
   Image = new SanityImage
   {
       Asset = new SanityReference<SanityImageAsset> { Ref = image.Id },            
   }
};

await sanity.DocumentSet<Author>().Create(author).CommitAsync();

   

7. Debugging

In order to see the raw GROQ query for a particular LINQ query, simply call GetSanityQuery().

var groq = sanity.DocumentSet<Post>().Where(p => p.PublishedAt >= DateTime.Today).GetSanityQuery();

GROQ queries can be tested directly in the Sanity UI using the Vision plugin: https://www.sanity.io/docs/front-ends/the-vision-plugin

8. Raw Client

The SanityClient class can be used for making "raw" GROQ requests to Sanity:

var client = new SanityClient(options);
await result = await client.FetchAsync("*[_type == "post"]");

9. Rendering Block Content

When you use the block editor in Sanity, it produces a structured array structure that you can use to render the content on any platform you might want. We have provided a class which can help you serialize your content to HTML, or potentially other formats using custom serializers. The SanityHTmlBuilder class has several inbuilt serializers, and new serializers can be added for custom Sanity types.

SanityHtmlBuilder can be accessed in several different ways:

// Create a stand-alone instance:
var builder = new SanityHtmlBuilder(Options);
var html = await builder.BuildAsync(myBlockContent); //Block content can be a string, JObject or POCO

// Access via existing SanityContext:
var sanity = new SanityDataContext(Options);
var post = await sanity.DocumentSet<Post>().FirstOrDefault();
var result = await sanity.HtmlBuilder.BuildAsync(post.Body);

// Use extension method directly on document (using Sanity.Linq.Extensions)
var sanity = new SanityDataContext(Options);
var post = await sanity.DocumentSet<Post>().FirstOrDefault();
var result = await post.Body.ToHtmlAsync(sanity);

You can also add your custom serializers to the SanityHtmlBuilder

sanity.HtmlBuilder.AddSerializer("myType", MySerializerFn);
// or
sanity.AddHtmlSerializer("myType", MySerializerFn);
// or 
builder.AddSerializer("myType", MySerializerFn);

The HTML builder supports serializing BlockContent arrays as well as single fields (such as an image field):

//This will check if the "_type" of the Body field has a serializer in the HtmlBuilder.Serializers dictionary and use it to return html.
var html = await htmlBuilder.BuildAsync(post.Body);
...
//strongly typed object
var html = await post.Body.ToHtmlAsync(sanity); // the whole content
var imageTag = await post.MainImage.ToHtmlAsync(sanity); // just a single block

Contribute

Feel free to submit pull-requests to the Sanity LINQ project!

Licence

The Sanity LINQ is available under the MIT Licence

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].