diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 8bfe54540..6e31851af 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -231,6 +231,7 @@ module.exports = { 'nextra-scrollbar', 'no-scrollbar', // from Nextra 'hive-slider', + 'hive-prose', 'subheader', ], config: path.join(__dirname, './packages/web/docs/tailwind.config.ts'), diff --git a/package.json b/package.json index 12dbcc07b..4c9c054ac 100644 --- a/package.json +++ b/package.json @@ -133,7 +133,9 @@ "@oclif/core@4.0.6": "patches/@oclif__core@4.0.6.patch", "@fastify/vite": "patches/@fastify__vite.patch", "p-cancelable@4.0.1": "patches/p-cancelable@4.0.1.patch", - "bentocache": "patches/bentocache.patch" + "bentocache": "patches/bentocache.patch", + "nextra": "patches/nextra.patch", + "nextra-theme-docs": "patches/nextra-theme-docs.patch" } } } diff --git a/packages/web/docs/package.json b/packages/web/docs/package.json index 3b64945ca..93947fe69 100644 --- a/packages/web/docs/package.json +++ b/packages/web/docs/package.json @@ -24,6 +24,7 @@ "react-countup": "6.5.3", "react-dom": "19.0.0", "react-icons": "5.4.0", + "rehype-frontmatter-mdx-imports": "0.1.1", "tailwind-merge": "2.6.0" }, "devDependencies": { diff --git a/packages/web/docs/src/app/_meta.ts b/packages/web/docs/src/app/_meta.ts index 52a18a6d2..b4e667621 100644 --- a/packages/web/docs/src/app/_meta.ts +++ b/packages/web/docs/src/app/_meta.ts @@ -82,7 +82,10 @@ const meta: Record> = { blog: { title: 'Blog', type: 'page', - href: 'https://the-guild.dev/blog', + theme: { + breadcrumb: false, + sidebar: false, + }, }, github: { title: 'GitHub', diff --git a/packages/web/docs/src/app/blog/(posts)/hive-platform-achieves-soc2-certification/page.mdx b/packages/web/docs/src/app/blog/(posts)/hive-platform-achieves-soc2-certification/page.mdx new file mode 100644 index 000000000..40d0e5f34 --- /dev/null +++ b/packages/web/docs/src/app/blog/(posts)/hive-platform-achieves-soc2-certification/page.mdx @@ -0,0 +1,93 @@ +--- +title: Hive Platform Achieves SOC-2 Type II Certification +tags: [security, cloud, hive, platform, compliance] +authors: dotan +date: 2025-03-25 +description: + The certification process involved a thorough review of our security controls and processes, and + we are proud to have achieved SOC-2 compliance. +featured: true +--- + +import { Callout } from '@theguild/components' + +We're excited to announce that Hive Platform (and The Guild) has successfully achieved SOC-2 Type II +certification for our flagship products. + +This milestone represents a significant step forward in our commitment to security, privacy, and +enterprise-grade reliability. + + + TL;DR: In [our Trust Center](https://security.graphql-hive.com) you can find all information about + security controls, documents and certificates. + + +## Overview + +After a decade of serving the GraphQL open-source community, The Guild has reached another important +milestone in our journey. We're proud to announce that we have successfully completed our SOC-2 Type +II audit, conducted by [Advantage Partners](https://advantage-partners.com/), using the +[Vanta](https://vanta.com/) compliance platform. + +As The Guild celebrates its 10th year of operations, this certification marks an important +investment in being enterprise-ready partner equipped to meet the rigorous security and compliance +requirements of organizations of all sizes. + +## What's SOC-2 Compliance? Why Type II Matters for Enterprises? + +[SOC-2](https://www.vanta.com/collection/soc-2/what-is-soc-2) (Service Organization Control 2) is a +voluntary compliance standard developed by the American Institute of CPAs (AICPA) that specifies how +organizations should manage customer data based on five "trust service criteria": security, +availability, processing integrity, confidentiality, and privacy. + +While SOC-2 Type I certifications assess whether a company's systems are suitably designed at a +specific point in time, **Type II certification** goes much further. It evaluates the operational +effectiveness of those controls over time, providing assurance that a company not only has +appropriate security measures in place but consistently follows them. + +For enterprises, this distinction is crucial: + +1. **Proven Reliability**: Type II demonstrates consistent adherence to security practices over + time, not just a one-time assessment +2. **Risk Reduction**: The certification significantly reduces the risk of data breaches and + security incidents +3. **Compliance Support**: It helps enterprises meet their own regulatory and internal compliance + requirements +4. **Vendor Management**: It simplifies vendor assessment processes, allowing for faster procurement + decisions +5. **Trust Verification**: It provides independent verification of security claims by a qualified + third party + +## What This Means for The Guild Customers + +For our customers, this certification brings several tangible benefits: + +### Enhanced Security and Privacy + +Our SOC-2 Type II certification confirms that we have robust controls in place to protect your data. + +This includes comprehensive security policies, regular testing, monitoring, and incident response +procedures that have been independently verified. + +### Simplified Vendor Assessment + +For enterprise customers with stringent security requirements, our certification streamlines the +procurement process. Instead of conducting extensive security assessments, you can rely on our SOC-2 +Type II report as evidence of our security posture. + +### Enterprise Readiness + +This certification demonstrates that The Guild is an organization that can confidently serve +enterprise customers with complex security and compliance requirements, while maintaining technical +excellence. + +--- + + + +We'd like to thank our team for their dedication to this process, our auditors at +[Advantage Partners](https://advantage-partners.com/) for their thorough assessment, and the +[Vanta](https://vanta.com/) platform for streamlining our compliance journey and making it easier +for us to achieve this certification. + + diff --git a/packages/web/docs/src/app/blog/(posts)/layout.tsx b/packages/web/docs/src/app/blog/(posts)/layout.tsx new file mode 100644 index 000000000..5f2fa8b69 --- /dev/null +++ b/packages/web/docs/src/app/blog/(posts)/layout.tsx @@ -0,0 +1,18 @@ +import { cn, HiveLayoutConfig } from '@theguild/components'; +import { LandingPageContainer } from '../../../components/landing-page-container'; +import '../../hive-prose-styles.css'; +import { BlogPostHeader } from '../components/blog-post-layout/blog-post-header'; + +const MAIN_CONTENT = 'main-content'; + +export default function BlogPostLayout({ children }: { children: React.ReactNode }) { + return ( + + + +
p:first-of-type]:text-2xl/8')}> + {children} +
+
+ ); +} diff --git a/packages/web/docs/src/app/blog/(posts)/understanding-the-differences-between-graphql-and-rest-api-gateways/page.mdx b/packages/web/docs/src/app/blog/(posts)/understanding-the-differences-between-graphql-and-rest-api-gateways/page.mdx new file mode 100644 index 000000000..565dc6e29 --- /dev/null +++ b/packages/web/docs/src/app/blog/(posts)/understanding-the-differences-between-graphql-and-rest-api-gateways/page.mdx @@ -0,0 +1,100 @@ +--- +title: Understanding the Differences Between GraphQL and REST API Gateways +tags: [graphql, rest] +authors: saihaj +date: 2024-12-03 +description: What is the difference between GraphQL and REST API Gateway? +featured: true +--- + +API gateways serve as crucial intermediaries between clients and backend services, but GraphQL and +REST gateways handle this responsibility quite differently. While a GraphQL gateway can be +considered a superset of REST gateway functionality, each has its distinct characteristics and use +cases. + +## Core Differences + +### Request Processing + +- **REST API Gateway**: Handles traditional HTTP requests with fixed endpoints. Each endpoint + typically serves a specific purpose and returns a predefined data structure. The gateway routes + these requests to appropriate microservices based on URL patterns. +- **GraphQL Gateway**: Processes queries written in the GraphQL query language, typically through a + single endpoint. It can understand complex queries requesting specific fields and relationships, + making it more flexible in handling varied data requirements. + +### Data Aggregation + +- **REST Gateway**: Often requires multiple endpoints to gather related data, leading to potential + over-fetching or under-fetching of data. The gateway might need to make several internal calls to + different services to compose a complete response. +- **GraphQL Gateway**: Excels at data aggregation by allowing clients to specify exactly what data + they need in a single request. The gateway can efficiently collect data from multiple services + based on the query structure. + +## Key Features and Capabilities + +### Caching + +- **REST Gateway**: Implements straightforward HTTP caching mechanisms. Responses can be cached + based on URLs and HTTP methods. +- **GraphQL Gateway**: Requires more sophisticated caching strategies due to the dynamic nature of + queries. Often implements field-level caching and needs to consider query complexity. + +### Security + +- **REST Gateway**: Security is typically implemented at the endpoint level with traditional + authentication and authorization mechanisms. +- **GraphQL Gateway**: Provides more granular security controls, allowing permissions to be set at + the field level. Can implement query complexity analysis to prevent abuse. + +### Schema Management + +- **REST Gateway**: No built-in schema management. API documentation typically relies on external + tools like Swagger/OpenAPI. +- **GraphQL Gateway**: Schema management can be as straightforward as maintaining schema definitions + in code and versioning them with Git. Teams can choose between simple code-first approaches or + leverage specialized tools like GraphQL Hive for more advanced schema registry and validation + features. This flexibility allows teams to scale their schema management practices as their needs + grow. + +### Service Integration + +- **REST Gateway**: No built-in way to integrate with other protocols. +- **GraphQL Gateway**: A GraphQL gateway like + [Hive Gateway](https://the-guild.dev/graphql/hive/docs/gateway?utm_source=the_guild&utm_medium=blog&utm_campaign=understanding-the-differences-between-graphql-and-rest-api-gateways) + unifies multiple protocols + ([REST](https://the-guild.dev/graphql/mesh/v1/source-handlers/openapi?utm_source=the_guild&utm_medium=blog&utm_campaign=understanding-the-differences-between-graphql-and-rest-api-gateways), + [gRPC](https://the-guild.dev/graphql/mesh/v1/source-handlers/grpc?utm_source=the_guild&utm_medium=blog&utm_campaign=understanding-the-differences-between-graphql-and-rest-api-gateways), + [SOAP](https://the-guild.dev/graphql/mesh/v1/source-handlers/soap?utm_source=the_guild&utm_medium=blog&utm_campaign=understanding-the-differences-between-graphql-and-rest-api-gateways) + & + [many more](https://the-guild.dev/graphql/mesh/v1/source-handlers?utm_source=the_guild&utm_medium=blog&utm_campaign=understanding-the-differences-between-graphql-and-rest-api-gateways)) + into a consistent interface using tools like + [GraphQL Mesh](https://the-guild.dev/graphql/mesh?utm_source=the_guild&utm_medium=blog&utm_campaign=understanding-the-differences-between-graphql-and-rest-api-gateways), + while supporting federation capabilities that let teams independently develop and deploy subgraphs + as part of a unified supergraph. Learn more about Federation + [here](https://the-guild.dev/graphql/hive/federation?utm_source=the_guild&utm_medium=blog&utm_campaign=understanding-the-differences-between-graphql-and-rest-api-gateways). + +## Why Choose GraphQL Gateway? + +GraphQL gateways represent the future of API architecture for several compelling reasons: + +1. **Enhanced Developer Experience**: GraphQL's intuitive query language and self-documenting nature + significantly improve developer productivity. +2. **Integration**: Easily integrate legacy services and offer a unified query experience. +3. **Optimal Performance**: By allowing clients to request exactly what they need, GraphQL + eliminates the over-fetching and under-fetching problems common with REST APIs. +4. **Future-Proof Architecture**: GraphQL's flexible schema system makes it easier to evolve your + API over time without breaking existing clients. +5. **Better Resource Utilization**: The ability to combine multiple data requirements into a single + request reduces server load and network overhead. +6. **Strong Ecosystem**: The GraphQL ecosystem offers excellent tools for monitoring, testing, and + managing your API gateway. + +## Conclusion + +While REST API gateways have served us well, GraphQL gateways offer superior capabilities for modern +applications. Their ability to handle complex data requirements efficiently, combined with excellent +developer experience and powerful tools, makes them the recommended choice for new API gateway +implementations. Organizations can start simple with basic schema management in Git and gradually +adopt more sophisticated tools like Hive as their needs evolve. diff --git a/packages/web/docs/src/app/blog/_meta.ts b/packages/web/docs/src/app/blog/_meta.ts new file mode 100644 index 000000000..725ff3f30 --- /dev/null +++ b/packages/web/docs/src/app/blog/_meta.ts @@ -0,0 +1,5 @@ +export default { + '*': { + display: 'hidden', + }, +}; diff --git a/packages/web/docs/src/app/blog/blog-types.ts b/packages/web/docs/src/app/blog/blog-types.ts new file mode 100644 index 000000000..af66fb7d7 --- /dev/null +++ b/packages/web/docs/src/app/blog/blog-types.ts @@ -0,0 +1,21 @@ +import type { StaticImageData } from 'next/image'; +import { AuthorId } from '../../authors'; +import { MdxFile, PageMapItem } from '../../mdx-types'; + +export interface BlogFrontmatter { + authors: AuthorId | AuthorId[]; + title: string; + date: string; + tags: string | string[]; + featured?: boolean; + image?: VideoPath | StaticImageData; + thumbnail?: StaticImageData; +} + +type VideoPath = `${string}.${'webm' | 'mp4'}`; + +export type BlogPostFile = Required>; + +export function isBlogPost(item: PageMapItem): item is BlogPostFile { + return item && 'route' in item && 'name' in item && 'frontMatter' in item && !!item.frontMatter; +} diff --git a/packages/web/docs/src/app/blog/components/blog-card.tsx b/packages/web/docs/src/app/blog/components/blog-card.tsx new file mode 100644 index 000000000..ab33af006 --- /dev/null +++ b/packages/web/docs/src/app/blog/components/blog-card.tsx @@ -0,0 +1,89 @@ +import Image from 'next/image'; +import { Anchor, cn } from '@theguild/components'; +import { Author, AuthorId, authors } from '../../../authors'; +import { BlogPostFile } from '../blog-types'; +import { BlogTagChip } from './blog-tag-chip'; + +export interface BlogCardProps { + post: Pick; + className?: string; + variant?: 'default' | 'featured'; + /** + * The tag to display on the card. If not provided, the first tag will be used. + * Used for tag index page, where we want to show all cards with the same tag. + */ + tag?: string | null; +} + +export function BlogCard({ post, className, variant, tag }: BlogCardProps) { + const frontmatter = post.frontMatter; + const { title, tags } = frontmatter; + const date = new Date(frontmatter.date); + + const postAuthors: Author[] = ( + typeof frontmatter.authors === 'string' + ? [authors[frontmatter.authors as AuthorId]] + : frontmatter.authors.map(author => authors[author as AuthorId]) + ).filter(Boolean); + + if (postAuthors.length === 0) { + console.error('author not found', frontmatter); + throw new Error(`authors ${JSON.stringify(frontmatter.authors)} not found`); + } + + const firstAuthor = postAuthors[0]; + + // todo: show more authors on hover? + const avatarSrc = + firstAuthor.avatar || `https://avatars.githubusercontent.com/${firstAuthor.github}?v=4&s=48`; + + return ( + +
+
+ + +
+

+ {title} +

+
+
+ {firstAuthor.name} +
+
+ {firstAuthor.name} +
+
+
+ ); +} diff --git a/packages/web/docs/src/app/blog/components/blog-page-hero.tsx b/packages/web/docs/src/app/blog/components/blog-page-hero.tsx new file mode 100644 index 000000000..f641aec1c --- /dev/null +++ b/packages/web/docs/src/app/blog/components/blog-page-hero.tsx @@ -0,0 +1,53 @@ +import { ArchDecoration, cn, DecorationIsolation, Heading } from '@theguild/components'; + +export function BlogPageHero({ className }: { className?: string }) { + return ( +
+ + + + + + + + + + + + + + + + + + GraphQL Stories + +

+ Explore insights on managing and optimizing your GraphQL APIs +

+
+ ); +} diff --git a/packages/web/docs/src/app/blog/components/blog-post-layout/blog-post-header.tsx b/packages/web/docs/src/app/blog/components/blog-post-layout/blog-post-header.tsx new file mode 100644 index 000000000..c118ba518 --- /dev/null +++ b/packages/web/docs/src/app/blog/components/blog-post-layout/blog-post-header.tsx @@ -0,0 +1,55 @@ +'use client'; + +import { Anchor, cn, Heading } from '@theguild/components'; +import { ArrowIcon } from '../../../../components/arrow-icon'; +import { useFrontmatter } from '../../../../components/use-frontmatter'; +import { ProductUpdateAuthors } from '../../../product-updates/(posts)/product-update-header'; +import type { BlogFrontmatter } from '../../blog-types'; +import { BlogTagChip } from '../blog-tag-chip'; +import { BlogPostPicture } from './blog-post-picture'; + +export function BlogPostHeader({ className }: { className?: string }) { + const { + frontmatter: { image, tags, authors, title, date }, + } = useFrontmatter(); + + const tag = tags[0]; + + return ( + <> + {image && } +
+
+ + + + Blog + {tag && /} + + + {tag && } +
+ + {title} + + +
+ + ); +} diff --git a/packages/web/docs/src/app/blog/components/blog-post-layout/blog-post-picture.tsx b/packages/web/docs/src/app/blog/components/blog-post-layout/blog-post-picture.tsx new file mode 100644 index 000000000..91d21d94c --- /dev/null +++ b/packages/web/docs/src/app/blog/components/blog-post-layout/blog-post-picture.tsx @@ -0,0 +1,20 @@ +import Image, { StaticImageData } from 'next/image'; +import { cn } from '@theguild/components'; + +export function BlogPostPicture({ + className, + image, +}: { + className?: string; + image: string | StaticImageData; +}) { + className = cn('h-[324px] rounded-3xl overflow-hidden object-cover', className); + + if (typeof image === 'string' && (image.endsWith('.webm') || image.endsWith('.mp4'))) { + return